Finalize implementation of Remote Connection Service (2/4)

Change-Id: I6525ff7d7e5fbb06a45d4811ebbc319a4b32fd6b
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index de59c06..4d73918 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -33,6 +33,12 @@
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
+    <!-- Protects the ability to register any PhoneAccount with a capability flags of either
+         PhoneAccount#CAPABILITY_CALL_PROVIDER or PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. -->
+    <permission
+            android:name="com.android.telecomm.permission.REGISTER_PROVIDER_OR_SUBSCRIPTION"
+            android:label="Register CALL_PROVIDER or SIM_SUBSCRIPTION PhoneAccount"
+            android:protectionLevel="signature"/>
 
     <!-- Declare which SDK level this application was built against. This is needed so that IDEs
          can check for incompatible APIs. -->
diff --git a/src/com/android/telecomm/ConnectionServiceWrapper.java b/src/com/android/telecomm/ConnectionServiceWrapper.java
index c791932..82381bd 100644
--- a/src/com/android/telecomm/ConnectionServiceWrapper.java
+++ b/src/com/android/telecomm/ConnectionServiceWrapper.java
@@ -860,6 +860,7 @@
         // Only give remote connection services to this connection service if it is listed as
         // the connection manager.
         PhoneAccountHandle simCallManager = registrar.getSimCallManager();
+        Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
         if (simCallManager == null ||
                 !simCallManager.getComponentName().equals(getComponentName())) {
             noRemoteServices(callback);
diff --git a/src/com/android/telecomm/CreateConnectionProcessor.java b/src/com/android/telecomm/CreateConnectionProcessor.java
index f3449ff..5a186f4 100644
--- a/src/com/android/telecomm/CreateConnectionProcessor.java
+++ b/src/com/android/telecomm/CreateConnectionProcessor.java
@@ -147,6 +147,8 @@
         }
         PhoneAccountHandle simCallManager =
                 TelecommApp.getInstance().getPhoneAccountRegistrar().getSimCallManager();
+
+        Log.d(this, "adjustAttemptsForWifi finds simCallManager = %s", simCallManager);
         if (simCallManager != null &&
                 !Objects.equals(simCallManager, mAttemptRecords.get(0).targetPhoneAccount)) {
             mAttemptRecords.set(
diff --git a/src/com/android/telecomm/TelecommServiceImpl.java b/src/com/android/telecomm/TelecommServiceImpl.java
index 3598422..9366f84 100644
--- a/src/com/android/telecomm/TelecommServiceImpl.java
+++ b/src/com/android/telecomm/TelecommServiceImpl.java
@@ -44,7 +44,8 @@
  * Implementation of the ITelecomm interface.
  */
 public class TelecommServiceImpl extends ITelecommService.Stub {
-    private static final String TELEPHONY_PACKAGE_NAME = "com.android.phone";
+    private static final String REGISTER_PROVIDER_OR_SUBSCRIPTION =
+            "com.android.telecomm.permission.REGISTER_PROVIDER_OR_SUBSCRIPTION";
 
     /** ${inheritDoc} */
     @Override
@@ -195,7 +196,7 @@
                     account.getAccountHandle().getComponentName().getPackageName());
             if (PhoneAccountRegistrar.has(account, PhoneAccount.CAPABILITY_CALL_PROVIDER) ||
                 PhoneAccountRegistrar.has(account, PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
-                enforceModifyPermissionOrCallingPackage(TELEPHONY_PACKAGE_NAME);
+                enforceRegisterProviderOrSubscriptionPermission();
             }
             mPhoneAccountRegistrar.registerPhoneAccount(account);
         } catch (Exception e) {
@@ -410,6 +411,11 @@
         }
     }
 
+    private void enforceRegisterProviderOrSubscriptionPermission() {
+        TelecommApp.getInstance().enforceCallingOrSelfPermission(
+                REGISTER_PROVIDER_OR_SUBSCRIPTION, null);
+    }
+
     private void enforceModifyPermissionOrCallingPackage(String packageName) {
         // TODO(santoscordon): Use a new telecomm permission for this instead of reusing modify.
         try {
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index fecfffb..bbc3292 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -15,10 +15,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.telecomm.tests">
+          coreApp="true"
+          package="com.android.telecomm.tests">
 
     <!-- Test connection service outgoing video preview. -->
     <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission
+            android:name="com.android.telecomm.permission.REGISTER_PROVIDER_OR_SUBSCRIPTION" />
 
     <application android:label="@string/app_name">
         <uses-library android:name="android.test.runner" />
@@ -31,6 +34,12 @@
             </intent-filter>
         </service>
 
+        <service android:name="com.android.telecomm.testapps.TestConnectionManager">
+            <intent-filter>
+                <action android:name="android.telecomm.ConnectionService" />
+            </intent-filter>
+        </service>
+
         <activity android:name="com.android.telecomm.testapps.TestCallActivity"
                 android:label="@string/testCallActivityLabel">
             <intent-filter>
diff --git a/tests/res/drawable-xhdpi/stat_sys_phone_call.png b/tests/res/drawable-xhdpi/stat_sys_phone_call.png
new file mode 100644
index 0000000..1bb4340
--- /dev/null
+++ b/tests/res/drawable-xhdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/tests/src/com/android/telecomm/testapps/CallServiceNotifier.java b/tests/src/com/android/telecomm/testapps/CallServiceNotifier.java
index 0dbb0b1..debfc9b 100644
--- a/tests/src/com/android/telecomm/testapps/CallServiceNotifier.java
+++ b/tests/src/com/android/telecomm/testapps/CallServiceNotifier.java
@@ -16,6 +16,8 @@
 
 package com.android.telecomm.testapps;
 
+import com.android.telecomm.tests.R;
+
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -88,19 +90,28 @@
      * Registers a phone account with telecomm.
      */
     public void registerPhoneAccount(Context context) {
-        PhoneAccount account = new PhoneAccount(
+        TelecommManager telecommManager =
+                (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
+        telecommManager.registerPhoneAccount(new PhoneAccount(
                 new PhoneAccountHandle(
                         new ComponentName(context, TestConnectionService.class),
                         PHONE_ACCOUNT_ID),
                 Uri.parse("tel:555-TEST"),
                 "555-TEST",
                 PhoneAccount.CAPABILITY_CALL_PROVIDER,
-                0,  // iconResId
+                R.drawable.stat_sys_phone_call,
                 "Dummy Service",
-                "a short description for the dummy service");
-        TelecommManager telecommManager =
-                (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
-        telecommManager.registerPhoneAccount(account);
+                "a short description for the dummy service"));
+        telecommManager.registerPhoneAccount(new PhoneAccount(
+                new PhoneAccountHandle(
+                        new ComponentName(context, TestConnectionManager.class),
+                        PHONE_ACCOUNT_ID),
+                Uri.parse("tel:555-CMGR"),
+                "555-CMGR",
+                PhoneAccount.CAPABILITY_CONNECTION_MANAGER,
+                R.drawable.stat_sys_phone_call,
+                "Dummy Connection Manager",
+                "a short description for the dummy connection manager"));
     }
 
     /**
diff --git a/tests/src/com/android/telecomm/testapps/TestConnectionManager.java b/tests/src/com/android/telecomm/testapps/TestConnectionManager.java
new file mode 100644
index 0000000..6a8433a
--- /dev/null
+++ b/tests/src/com/android/telecomm/testapps/TestConnectionManager.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telecomm.testapps;
+
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.telecomm.CallAudioState;
+import android.telecomm.Connection;
+import android.telecomm.ConnectionRequest;
+import android.telecomm.ConnectionService;
+import android.telecomm.PhoneAccountHandle;
+import android.telecomm.RemoteConnection;
+import android.telecomm.StatusHints;
+import android.util.Log;
+
+import java.util.Random;
+
+/**
+ * Service which acts as a fake ConnectionManager if so configured.
+ * TODO(santoscordon): Rename all classes in the directory to Dummy* (e.g., DummyConnectionService).
+ */
+public class TestConnectionManager extends ConnectionService {
+    /**
+     * Random number generator used to generate phone numbers.
+     */
+    private Random mRandom = new Random();
+
+    private final class TestManagedConnection extends Connection {
+        private final RemoteConnection.Listener mProxyListener = new RemoteConnection.Listener() {
+            @Override
+            public void onStateChanged(RemoteConnection connection, int state) {
+                setState(state);
+            }
+
+            @Override
+            public void onDisconnected(RemoteConnection connection, int cause, String message) {
+                setDisconnected(cause, message);
+                destroy();
+            }
+
+            @Override
+            public void onRequestingRingback(RemoteConnection connection, boolean ringback) {
+                setRequestingRingback(ringback);
+            }
+
+            @Override
+            public void onCallCapabilitiesChanged(RemoteConnection connection,
+                    int callCapabilities) {
+                setCallCapabilities(callCapabilities);
+            }
+
+            @Override
+            public void onPostDialWait(RemoteConnection connection, String remainingDigits) {
+                setPostDialWait(remainingDigits);
+            }
+
+            @Override
+            public void onAudioModeIsVoipChanged(RemoteConnection connection, boolean isVoip) {
+                setAudioModeIsVoip(isVoip);
+            }
+
+            @Override
+            public void onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints) {
+                setStatusHints(statusHints);
+            }
+
+            @Override
+            public void onVideoStateChanged(RemoteConnection connection, int videoState) {
+                setVideoState(videoState);
+            }
+
+            @Override
+            public void onHandleChanged(RemoteConnection connection, Uri handle, int presentation) {
+                setHandle(handle, presentation);
+            }
+
+            @Override
+            public void onCallerDisplayNameChanged(
+                    RemoteConnection connection, String callerDisplayName, int presentation) {
+                setCallerDisplayName(callerDisplayName, presentation);
+            }
+
+            @Override
+            public void onStartActivityFromInCall(
+                    RemoteConnection connection, PendingIntent intent) {
+                startActivityFromInCall(intent);
+            }
+
+            @Override
+            public void onDestroyed(RemoteConnection connection) {
+                destroy();
+            }
+        };
+
+        private final RemoteConnection mRemoteConnection;
+        private final boolean mIsIncoming;
+
+        TestManagedConnection(RemoteConnection remoteConnection, boolean isIncoming) {
+            mRemoteConnection = remoteConnection;
+            mIsIncoming = isIncoming;
+            mRemoteConnection.addListener(mProxyListener);
+            setState(mRemoteConnection.getState());
+        }
+
+        @Override
+        public void onAbort() {
+            mRemoteConnection.abort();
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onAnswer(int videoState) {
+            mRemoteConnection.answer(videoState);
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onDisconnect() {
+            mRemoteConnection.disconnect();
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onHold() {
+            mRemoteConnection.hold();
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onReject() {
+            mRemoteConnection.reject();
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onUnhold() {
+            mRemoteConnection.unhold();
+        }
+
+        @Override
+        public void onSetAudioState(CallAudioState state) {
+            mRemoteConnection.setAudioState(state);
+        }
+
+        private void setState(int state) {
+            log("setState: " + state);
+            switch (state) {
+                case State.ACTIVE:
+                    setActive();
+                    break;
+                case State.HOLDING:
+                    setOnHold();
+                    break;
+                case State.DIALING:
+                    setDialing();
+                    break;
+                case State.RINGING:
+                    setRinging();
+                    break;
+            }
+        }
+    }
+
+    private static void log(String msg) {
+        Log.w("telecomtestcs", "[TestConnectionService] " + msg);
+    }
+
+    @Override
+    public Connection onCreateOutgoingConnection(
+            PhoneAccountHandle connectionManagerAccount,
+            final ConnectionRequest request) {
+        return new TestManagedConnection(
+                createRemoteOutgoingConnection(
+                        request.getAccountHandle(),
+                        request),
+                false);
+    }
+
+    @Override
+    public Connection onCreateIncomingConnection(
+            PhoneAccountHandle connectionManagerAccount,
+            final ConnectionRequest request) {
+        return new TestManagedConnection(
+                createRemoteOutgoingConnection(
+                        request.getAccountHandle(),
+                        request),
+                true);
+    }
+}