start/stop in-call UI on demand

This change severs the connection to the in-call UI whenever there is no
active call.  When we get a new call, we attempt to the bind to the
UI service.  Whenever we lose/disconnect a call, we see if there are any calls
left and if not, unbind from the UI service.

Changes:
- Add explicit component name to config.xml and use them when binding to
  the UI service.
- onUpdate and onIncomingCall in CAllHandlerServiceProxy now make a call
  to maybeBindToService() in case the service requires binding.
- onDisconnect calls maybeUnbind() after a disconnect.
- add cleanup of current binding to onServiceDisconnect().  Also attempt
  to rebind if there are still active calls.

bug: 10363682
Change-Id: I7ddbb33fde7e3b03c1f1ea759d174cb03baabc76
diff --git a/src/com/android/phone/CallHandlerServiceProxy.java b/src/com/android/phone/CallHandlerServiceProxy.java
index 77402fd..12e039e 100644
--- a/src/com/android/phone/CallHandlerServiceProxy.java
+++ b/src/com/android/phone/CallHandlerServiceProxy.java
@@ -62,12 +62,8 @@
         mCallModeler = callModeler;
         mAudioRouter = audioRouter;
 
-        setupServiceConnection();
         mAudioRouter.addAudioModeListener(this);
         mCallModeler.addListener(this);
-
-        // start the whole process
-        onUpdate(mCallModeler.getFullList(), true);
     }
 
     @Override
@@ -76,6 +72,7 @@
             try {
                 if (DBG) Log.d(TAG, "onDisconnect: " + call);
                 mCallHandlerService.onDisconnect(call);
+                maybeUnbind();
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote exception handling onDisconnect ", e);
             }
@@ -84,7 +81,7 @@
 
     @Override
     public void onIncoming(Call call, ArrayList<String> textResponses) {
-        if (mCallHandlerService != null) {
+        if (maybeBindToService() && mCallHandlerService != null) {
             try {
                 if (DBG) Log.d(TAG, "onIncoming: " + call);
                 mCallHandlerService.onIncoming(call, textResponses);
@@ -96,10 +93,11 @@
 
     @Override
     public void onUpdate(List<Call> calls, boolean fullUpdate) {
-        if (mCallHandlerService != null) {
+        if (maybeBindToService() && mCallHandlerService != null) {
             try {
                 if (DBG) Log.d(TAG, "onUpdate: " + calls.toString());
                 mCallHandlerService.onUpdate(calls, fullUpdate);
+                maybeUnbind();
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote exception handling onUpdate", e);
             }
@@ -151,22 +149,58 @@
 
             @Override
             public void onServiceDisconnected(ComponentName className) {
-                // TODO(klp): handle the case where the in call ui crashed or gets destroyed.
-                // In the near term, we need to re-bind to the service when ever it's gone.
-                // Longer term, we need a way to catch the crash and allow the users to choose
-                // a different in-call screen.
-                Log.e(TAG, "Yikes! no in call ui!");
+                Log.i(TAG, "Disconnected from UI service.");
                 mCallHandlerService = null;
+
+                // clean up our current binding.
+                mContext.unbindService(mConnection);
+                mConnection = null;
+
+                // potentially attempt to rebind if there are still active calls.
+                maybeBindToService();
             }
         };
 
-        Intent serviceIntent = new Intent(ICallHandlerService.class.getName());
+        final Intent serviceIntent = new Intent(ICallHandlerService.class.getName());
+        final ComponentName component = new ComponentName(
+                mContext.getResources().getString(R.string.incall_ui_default_package),
+                mContext.getResources().getString(R.string.incall_ui_default_class));
+        serviceIntent.setComponent(component);
         if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {
             Log.e(TAG, "Cound not bind to ICallHandlerService");
         }
     }
 
     /**
+     * Checks To see if there are any calls left.  If not, unbind the callhandler service.
+     */
+    private void maybeUnbind() {
+        if (!mCallModeler.hasLiveCall()) {
+            if (mConnection != null) {
+                mContext.unbindService(mConnection);
+                mConnection = null;
+            }
+        }
+    }
+
+    /**
+     * Checks to see if there are any active calls.  If so, binds the call handler service.
+     * @return true if already bound. False otherwise.
+     */
+    private boolean maybeBindToService() {
+        if (mCallModeler.hasLiveCall()) {
+            // mConnection is set to non-null once an attempt is made to connect.
+            // We do not check against mCallHandlerService here because we could potentially
+            // create multiple bindings to the UI.
+            if (mConnection != null) {
+                return true;
+            }
+            setupServiceConnection();
+        }
+        return false;
+    }
+
+    /**
      * Called when the in-call UI service is connected.  Send command interface to in-call.
      */
     private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) {
@@ -174,6 +208,9 @@
 
         try {
             mCallHandlerService.setCallCommandService(mCallCommandService);
+
+            // start with a full update
+            onUpdate(mCallModeler.getFullList(), true);
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception calling CallHandlerService::setCallCommandService", e);
         }