Update connection service iteration for PhoneAccounts

Change-Id: I8f558439016388540af0deaf7c621cff89c9cb36
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index 2a10558..0eeb21e 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -80,6 +80,7 @@
         void onCallerDisplayNameChanged(Call call);
         void onVideoStateChanged(Call call);
         void onStartActivityFromInCall(Call call, PendingIntent intent);
+        void onPhoneAccountChanged(Call call);
     }
 
     abstract static class ListenerBase implements Listener {
@@ -125,6 +126,8 @@
         public void onVideoStateChanged(Call call) {}
         @Override
         public void onStartActivityFromInCall(Call call, PendingIntent intent) {}
+        @Override
+        public void onPhoneAccountChanged(Call call) {}
     }
 
     private static final OnQueryCompleteListener sCallerInfoQueryListener =
@@ -440,7 +443,12 @@
     }
 
     void setPhoneAccount(PhoneAccount account) {
-        mPhoneAccount = account;
+        if (!Objects.equals(mPhoneAccount, account)) {
+            mPhoneAccount = account;
+            for (Listener l : mListeners) {
+                l.onPhoneAccountChanged(this);
+            }
+        }
     }
 
     boolean isIncoming() {
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index b8733c5..5a53525 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -311,7 +311,11 @@
         call.addListener(this);
         addCall(call);
 
-        if (account == null) {
+        if (TelephonyUtil.shouldProcessAsEmergency(TelecommApp.getInstance(), call.getHandle())) {
+            // Emergency -- CreateConnectionProcessor will choose accounts automatically
+            call.setPhoneAccount(null);
+            call.startCreateConnection();
+        } else if (account == null) {
             PhoneAccount defaultAccount = TelecommApp.getInstance().getPhoneAccountRegistrar()
                     .getDefaultOutgoingPhoneAccount();
             if (defaultAccount != null) {
diff --git a/src/com/android/telecomm/CreateConnectionProcessor.java b/src/com/android/telecomm/CreateConnectionProcessor.java
index 5d1f308..45008c0 100644
--- a/src/com/android/telecomm/CreateConnectionProcessor.java
+++ b/src/com/android/telecomm/CreateConnectionProcessor.java
@@ -16,10 +16,8 @@
 
 package com.android.telecomm;
 
-import android.content.ComponentName;
-import android.net.Uri;
+import android.telecomm.PhoneAccount;
 import android.telephony.DisconnectCause;
-import android.telephony.PhoneNumberUtils;
 import android.telecomm.ConnectionRequest;
 
 import java.util.ArrayList;
@@ -36,8 +34,8 @@
 final class CreateConnectionProcessor {
     private final Call mCall;
     private final ConnectionServiceRepository mRepository;
-    private List<ComponentName> mServiceComponentNames;
-    private Iterator<ComponentName> mServiceComponentNameIterator;
+    private List<PhoneAccount> mPhoneAccounts;
+    private Iterator<PhoneAccount> mPhoneAccountIterator;
     private CreateConnectionResponse mResponse;
     private int mLastErrorCode = DisconnectCause.ERROR_UNSPECIFIED;
     private String mLastErrorMsg;
@@ -51,29 +49,13 @@
 
     void process() {
         Log.v(this, "process");
-
-        mServiceComponentNames = new ArrayList<>();
-
-        // TODO(sail): Remove once there's a way to pick the service.
-        ArrayList<ComponentName> priorityComponents = new ArrayList<>();
-        priorityComponents.add(new ComponentName("com.google.android.talk",
-                "com.google.android.apps.babel.telephony.TeleConnectionService"));
-        priorityComponents.add(new ComponentName("com.android.telecomm.tests",
-                "com.android.telecomm.testapps.TestConnectionService"));
-
-        for (ConnectionServiceWrapper service : mRepository.lookupServices()) {
-            ComponentName serviceName = service.getComponentName();
-            if (priorityComponents.contains(serviceName)) {
-                Log.i(this, "Moving connection service %s to top of list", serviceName);
-                mServiceComponentNames .add(0, serviceName);
-            } else {
-                mServiceComponentNames.add(serviceName);
-            }
+        mPhoneAccounts = new ArrayList<>();
+        if (mCall.getPhoneAccount() != null) {
+            mPhoneAccounts.add(mCall.getPhoneAccount());
         }
-
-        adjustComponentNamesForEmergency();
-        mServiceComponentNameIterator = mServiceComponentNames.iterator();
-        attemptNextConnectionService();
+        adjustPhoneAccountsForEmergency();
+        mPhoneAccountIterator = mPhoneAccounts.iterator();
+        attemptNextPhoneAccount();
     }
 
     void abort() {
@@ -94,21 +76,24 @@
         }
     }
 
-    private void attemptNextConnectionService() {
-        Log.v(this, "attemptNextConnectionService");
+    private void attemptNextPhoneAccount() {
+        Log.v(this, "attemptNextPhoneAccount");
 
-        if (mResponse != null && mServiceComponentNameIterator.hasNext()) {
-            ComponentName component = mServiceComponentNameIterator.next();
-            ConnectionServiceWrapper service = mRepository.getService(component);
+        if (mResponse != null && mPhoneAccountIterator.hasNext()) {
+            PhoneAccount account = mPhoneAccountIterator.next();
+            Log.i(this, "Trying account %s", account);
+            ConnectionServiceWrapper service = mRepository.getService(account.getComponentName());
             if (service == null) {
-                attemptNextConnectionService();
+                Log.i(this, "Found no connection service for account %s", account);
+                attemptNextPhoneAccount();
             } else {
+                mCall.setPhoneAccount(account);
                 mCall.setConnectionService(service);
                 Log.i(this, "Attempting to call from %s", service.getComponentName());
                 service.createConnection(mCall, new Response(service));
             }
         } else {
-            Log.v(this, "attemptNextConnectionService, no more services, failing");
+            Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
             if (mResponse != null) {
                 mResponse.handleCreateConnectionFailed(mLastErrorCode, mLastErrorMsg);
                 mResponse = null;
@@ -118,23 +103,22 @@
     }
 
     // If we are possibly attempting to call a local emergency number, ensure that the
-    // plain PSTN connection service, if it exists, is attempted first.
-    private void adjustComponentNamesForEmergency()  {
-        if (shouldProcessAsEmergency(mCall.getHandle())) {
-            for (int i = 0; i < mServiceComponentNames.size(); i++) {
-                if (TelephonyUtil.isPstnComponentName(mServiceComponentNames.get(i))) {
-                    mServiceComponentNames.add(0, mServiceComponentNames.remove(i));
-                    return;
+    // plain PSTN connection services are listed, and nothing else.
+    private void adjustPhoneAccountsForEmergency()  {
+        if (TelephonyUtil.shouldProcessAsEmergency(TelecommApp.getInstance(), mCall.getHandle())) {
+            Log.i(this, "Emergency number detected");
+            mPhoneAccounts.clear();
+            List<PhoneAccount> allAccounts = TelecommApp.getInstance().getPhoneAccountRegistrar()
+                    .getEnabledPhoneAccounts();
+            for (int i = 0; i < allAccounts.size(); i++) {
+                if (TelephonyUtil.isPstnComponentName(allAccounts.get(i).getComponentName())) {
+                    Log.i(this, "Will try PSTN account %s for emergency", allAccounts.get(i));
+                    mPhoneAccounts.add(allAccounts.get(i));
                 }
             }
         }
     }
 
-    private boolean shouldProcessAsEmergency(Uri handle) {
-        return handle != null && PhoneNumberUtils.isPotentialLocalEmergencyNumber(
-                TelecommApp.getInstance(), handle.getSchemeSpecificPart());
-    }
-
     private class Response implements CreateConnectionResponse {
         private final ConnectionServiceWrapper mService;
 
@@ -156,7 +140,8 @@
         public void handleCreateConnectionFailed(int code, String msg) {
             mLastErrorCode = code;
             mLastErrorMsg = msg;
-            attemptNextConnectionService();
+            Log.d(CreateConnectionProcessor.this, "Connection failed: %d (%s)", code, msg);
+            attemptNextPhoneAccount();
         }
 
         @Override
diff --git a/src/com/android/telecomm/InCallController.java b/src/com/android/telecomm/InCallController.java
index 08025a9..eeef42c 100644
--- a/src/com/android/telecomm/InCallController.java
+++ b/src/com/android/telecomm/InCallController.java
@@ -107,6 +107,11 @@
                 }
             }
         }
+
+        @Override
+        public void onPhoneAccountChanged(Call call) {
+            updateCall(call);
+        }
     };
 
     /** Maintains a binding connection to the in-call app. */
diff --git a/src/com/android/telecomm/TelecommApp.java b/src/com/android/telecomm/TelecommApp.java
index b02a8f8..fd35708 100644
--- a/src/com/android/telecomm/TelecommApp.java
+++ b/src/com/android/telecomm/TelecommApp.java
@@ -17,7 +17,12 @@
 package com.android.telecomm;
 
 import android.app.Application;
+import android.content.ComponentName;
+import android.net.Uri;
 import android.os.UserHandle;
+import android.telecomm.PhoneAccount;
+import android.telecomm.PhoneAccountMetadata;
+import android.telephony.PhoneNumberUtils;
 
 /**
  * Top-level Application class for Telecomm.
@@ -46,6 +51,8 @@
         mMissedCallNotifier = new MissedCallNotifier(this);
         mPhoneAccountRegistrar = new PhoneAccountRegistrar(this);
 
+        addHangoutsAccount();
+
         if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
             TelecommServiceImpl.init(mMissedCallNotifier, mPhoneAccountRegistrar);
         }
@@ -65,4 +72,25 @@
     PhoneAccountRegistrar getPhoneAccountRegistrar() {
         return mPhoneAccountRegistrar;
     }
+
+    private void addHangoutsAccount() {
+        // TODO: STOPSHIP. We are adding a hacked PhoneAccount to ensure that Wi-Fi calling in
+        // Hangouts continues to work. This needs to be replaced with proper Wi-Fi calling wiring
+        // to the appropriate Connection Services.
+        PhoneAccountMetadata hangouts = new PhoneAccountMetadata(
+                new PhoneAccount(
+                        new ComponentName(
+                                "com.google.android.talk",
+                                "com.google.android.apps.babel.telephony.TeleConnectionService"),
+                        "null_id"),
+                Uri.fromParts("tel", "null_uri", null),
+                PhoneAccountMetadata.CAPABILITY_CALL_PROVIDER,
+                R.drawable.stat_sys_phone_call,
+                "Wi-Fi calling",
+                "Wi-Fi calling by Google Hangouts",
+                false);
+        mPhoneAccountRegistrar.clearAccounts(
+                hangouts.getAccount().getComponentName().getPackageName());
+        mPhoneAccountRegistrar.registerPhoneAccount(hangouts);
+    }
 }
diff --git a/src/com/android/telecomm/TelephonyUtil.java b/src/com/android/telecomm/TelephonyUtil.java
index ac19525..2d22491 100644
--- a/src/com/android/telecomm/TelephonyUtil.java
+++ b/src/com/android/telecomm/TelephonyUtil.java
@@ -17,9 +17,9 @@
 package com.android.telecomm;
 
 import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.telecomm.PhoneAccount;
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.PhoneNumberUtils;
 
 /**
  * Utilities to deal with the system telephony services. The system telephony services are treated
@@ -31,7 +31,7 @@
     private static final String TELEPHONY_PACKAGE_NAME = "com.android.phone";
 
     private static final String PSTN_CALL_SERVICE_CLASS_NAME =
-            "com.android.services.telephony.PstnConnectionService";
+            "com.android.services.telephony.TelephonyConnectionService";
 
     private TelephonyUtil() {}
 
@@ -41,17 +41,8 @@
         return pstnComponentName.equals(componentName);
     }
 
-    private static void verifyConnectionServiceExists(String serviceName) {
-        PackageManager packageManager = TelecommApp.getInstance().getPackageManager();
-        try {
-            ServiceInfo info = packageManager.getServiceInfo(
-                    new ComponentName(TELEPHONY_PACKAGE_NAME, serviceName), 0);
-            if (info == null) {
-                Log.wtf(TAG, "Error, unable to find connection service: %s", serviceName);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.wtf(TAG, e, "Error, exception while trying to find connection service: %s",
-                    serviceName);
-        }
+    static boolean shouldProcessAsEmergency(Context context, Uri handle) {
+        return handle != null && PhoneNumberUtils.isPotentialLocalEmergencyNumber(
+                context, handle.getSchemeSpecificPart());
     }
 }