Merge "Updating reconnect retry code for InCallUI." into klp-dev
diff --git a/src/com/android/phone/CallHandlerServiceProxy.java b/src/com/android/phone/CallHandlerServiceProxy.java
index 6670ae4..7c8ace7 100644
--- a/src/com/android/phone/CallHandlerServiceProxy.java
+++ b/src/com/android/phone/CallHandlerServiceProxy.java
@@ -56,8 +56,9 @@
             "ro.debuggable", 0) == 1);
 
     public static final int RETRY_DELAY_MILLIS = 2000;
+    public static final int RETRY_DELAY_LONG_MILLIS = 30 * 1000; // 30 seconds
     private static final int BIND_RETRY_MSG = 1;
-    private static final int MAX_RETRY_COUNT = 5;
+    private static final int MAX_SHORT_DELAY_RETRY_COUNT = 5;
 
     private AudioRouter mAudioRouter;
     private CallCommandService mCallCommandService;
@@ -78,7 +79,7 @@
 
         switch (msg.what) {
             case BIND_RETRY_MSG:
-                setupServiceConnection();
+                handleConnectRetry();
                 break;
         }
     }
@@ -140,6 +141,9 @@
 
     @Override
     public void onIncoming(Call call) {
+        // for new incoming calls, reset the retry count.
+        resetConnectRetryCount();
+
         synchronized (mServiceAndQueueLock) {
             if (mCallHandlerServiceGuarded == null) {
                 if (DBG) {
@@ -286,7 +290,7 @@
                 Log.d(TAG, "Service Connected");
             }
             onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service));
-            mBindRetryCount = 0;
+            resetConnectRetryCount();
         }
 
         @Override public void onServiceDisconnected (ComponentName className){
@@ -343,6 +347,7 @@
             if (mConnection == null) {
                 mConnection = new InCallServiceConnection();
 
+                boolean failedConnection = false;
                 final PackageManager packageManger = mContext.getPackageManager();
                 final List<ResolveInfo> services = packageManger.queryIntentServices(serviceIntent,
                         0);
@@ -365,50 +370,95 @@
                     // This can happen if the service is being installed by the package manager.
                     // Between deletes and installs, bindService could get a silent service not
                     // found error.
-                    mBindRetryCount++;
-                    if (mBindRetryCount < MAX_RETRY_COUNT) {
-                        Log.w(TAG, "InCallUI service not found. " + serviceIntent
-                                + ". This happens if the service is being installed and should be"
-                                + " transient. Retrying" + RETRY_DELAY_MILLIS + " ms.");
-                        sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
-                                RETRY_DELAY_MILLIS);
-                    } else {
-                        Log.e(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
-                                + " Giving up.");
+                    Log.w(TAG, "Default call handler service not found.");
+                    failedConnection = true;
+                } else {
+
+                    serviceIntent.setComponent(new ComponentName(serviceInfo.packageName,
+                            serviceInfo.name));
+                    if (DBG) {
+                        Log.d(TAG, "binding to service " + serviceIntent);
                     }
-                    return;
-                }
-
-                // Bind to the first service that has a permission
-                // TODO: Add UI to allow us to select between services
-
-                serviceIntent.setComponent(new ComponentName(serviceInfo.packageName,
-                        serviceInfo.name));
-                if (DBG) {
-                    Log.d(TAG, "binding to service " + serviceIntent);
-                }
-                if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {
-                    // This happens when the in-call package is in the middle of being
-                    // installed.
-                    // Delay the retry.
-                    mBindRetryCount++;
-                    if (mBindRetryCount < MAX_RETRY_COUNT) {
-                        Log.e(TAG, "bindService failed on " + serviceIntent + ".  Retrying in "
-                                + RETRY_DELAY_MILLIS + " ms.");
-                        sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
-                                RETRY_DELAY_MILLIS);
-                    } else {
-                        Log.wtf(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
-                                + " Giving up.");
+                    if (!mContext.bindService(serviceIntent, mConnection,
+                            Context.BIND_AUTO_CREATE)) {
+                        // This happens when the in-call package is in the middle of being installed
+                        Log.w(TAG, "Could not bind to default call handler service: " +
+                                serviceIntent.getComponent());
+                        failedConnection = true;
                     }
                 }
 
+                if (failedConnection) {
+                    mConnection = null;
+                    enqueueConnectRetry();
+                }
             } else {
                 Log.d(TAG, "Service connection to in call service already started.");
             }
         }
     }
 
+    private void resetConnectRetryCount() {
+        mBindRetryCount = 0;
+    }
+
+    private void incrementRetryCount() {
+        // Reset to the short delay retry count to avoid overflow
+        if (Integer.MAX_VALUE == mBindRetryCount) {
+            mBindRetryCount = MAX_SHORT_DELAY_RETRY_COUNT;
+        }
+
+        mBindRetryCount++;
+    }
+
+    private void handleConnectRetry() {
+        // Remove any pending messages since we're already performing the action.
+        // If the call to setupServiceConnection() fails, it will queue up another retry.
+        removeMessages(BIND_RETRY_MSG);
+
+        // Something else triggered the connection, cancel.
+        if (mConnection != null) {
+            Log.i(TAG, "Retry: already connected.");
+            return;
+        }
+
+        if (mCallModeler.hasLiveCall()) {
+            // Update the count when we are actually trying the retry instead of when the
+            // retry is queued up.
+            incrementRetryCount();
+
+            Log.i(TAG, "Retrying connection: " + mBindRetryCount);
+            setupServiceConnection();
+        } else {
+            Log.i(TAG, "Canceling connection retry since there are no calls.");
+            // We are not currently connected and there is no call so lets not bother
+            // with the retry. Also, empty the queue of pending messages to send
+            // to the UI.
+            synchronized (mServiceAndQueueLock) {
+                if (mQueue != null) {
+                    mQueue.clear();
+                }
+            }
+
+            // Since we have no calls, reset retry count.
+            resetConnectRetryCount();
+        }
+    }
+
+    /**
+     * Called after the connection failed and a retry is needed.
+     * Queues up a retry to happen with a delay.
+     */
+    private void enqueueConnectRetry() {
+        final boolean isLongDelay = (mBindRetryCount > MAX_SHORT_DELAY_RETRY_COUNT);
+        final int delay = isLongDelay ? RETRY_DELAY_LONG_MILLIS : RETRY_DELAY_MILLIS;
+
+        Log.w(TAG, "InCallUI Connection failed. Enqueuing delayed retry for " + delay + " ms." +
+                " retries(" + mBindRetryCount + ")");
+
+        sendEmptyMessageDelayed(BIND_RETRY_MSG, delay);
+    }
+
     private void unbind() {
         synchronized (mServiceAndQueueLock) {
             // On unbind, reenable the notification shade and navigation bar just in case the