Add bind timeout to CarrierConfigLoader.

Binding to a service can fail in ways that don't trigger
ServiceConnection#onServiceConnected or #onServiceDisconnected.

This adds a timeout to binds so that any of these failures will be
treated the same as failing to bind to a CarrierService.

Bug: 21614274
Change-Id: Ic18d1d0cd227175f2943f935e06e6aeae35009e0
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 2cf5276..544b7b2 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -111,6 +111,12 @@
     private static final int EVENT_FETCH_CARRIER = 8;
     // A package has been installed, uninstalled, or updated.
     private static final int EVENT_PACKAGE_CHANGED = 9;
+    // Bind timed out for the default app.
+    private static final int EVENT_BIND_DEFAULT_TIMEOUT = 10;
+    // Bind timed out for a carrier app.
+    private static final int EVENT_BIND_CARRIER_TIMEOUT = 11;
+
+    private static final int BIND_TIMEOUT_MILLIS = 10000;
 
     // Tags used for saving and restoring XML documents.
     private static final String TAG_DOCUMENT = "carrier_config";
@@ -175,8 +181,11 @@
                         newMsg.getData().putBoolean("loaded_from_xml", true);
                         mHandler.sendMessage(newMsg);
                     } else {
-                        if (!bindToConfigPackage(DEFAULT_CARRIER_CONFIG_PACKAGE,
+                        if (bindToConfigPackage(DEFAULT_CARRIER_CONFIG_PACKAGE,
                                 phoneId, EVENT_CONNECTED_TO_DEFAULT)) {
+                            sendMessageDelayed(obtainMessage(EVENT_BIND_DEFAULT_TIMEOUT, phoneId, -1),
+                                    BIND_TIMEOUT_MILLIS);
+                        } else {
                             // Send bcast if bind fails
                             broadcastConfigChangedIntent(phoneId);
                         }
@@ -184,6 +193,7 @@
                     break;
 
                 case EVENT_CONNECTED_TO_DEFAULT:
+                    removeMessages(EVENT_BIND_DEFAULT_TIMEOUT);
                     carrierId = getCarrierIdForPhoneId(phoneId);
                     conn = (CarrierServiceConnection) msg.obj;
                     // If new service connection has been created, unbind.
@@ -206,6 +216,11 @@
                     }
                     break;
 
+                case EVENT_BIND_DEFAULT_TIMEOUT:
+                    mContext.unbindService(mServiceConnection[phoneId]);
+                    broadcastConfigChangedIntent(phoneId);
+                    break;
+
                 case EVENT_LOADED_FROM_DEFAULT:
                     // If we attempted to bind to the app, but the service connection is null, then
                     // config was cleared while we were waiting and we should not continue.
@@ -234,8 +249,11 @@
                         newMsg.getData().putBoolean("loaded_from_xml", true);
                         sendMessage(newMsg);
                     } else {
-                        if (!bindToConfigPackage(carrierPackageName, phoneId,
+                        if (bindToConfigPackage(carrierPackageName, phoneId,
                                 EVENT_CONNECTED_TO_CARRIER)) {
+                            sendMessageDelayed(obtainMessage(EVENT_BIND_CARRIER_TIMEOUT, phoneId, -1),
+                                    BIND_TIMEOUT_MILLIS);
+                        } else {
                             // Send bcast if bind fails
                             broadcastConfigChangedIntent(phoneId);
                         }
@@ -243,6 +261,7 @@
                     break;
 
                 case EVENT_CONNECTED_TO_CARRIER:
+                    removeMessages(EVENT_BIND_CARRIER_TIMEOUT);
                     carrierId = getCarrierIdForPhoneId(phoneId);
                     conn = (CarrierServiceConnection) msg.obj;
                     // If new service connection has been created, unbind.
@@ -267,6 +286,11 @@
                     }
                     break;
 
+                case EVENT_BIND_CARRIER_TIMEOUT:
+                    mContext.unbindService(mServiceConnection[phoneId]);
+                    broadcastConfigChangedIntent(phoneId);
+                    break;
+
                 case EVENT_LOADED_FROM_CARRIER:
                     // If we attempted to bind to the app, but the service connection is null, then
                     // config was cleared while we were waiting and we should not continue.