Fix potential issue where sub info change listener is not registered.
SubscriptionManager#addOnSubscriptionsChangedListener can fail to
register a listener if the TELEPHONY_REGISTRY system service is not up.
Currently this is just silently ignored.
Adding a callback method on the listener to notify the registrant that the
listener failed to be registered, and adding exponential backoff code in
TelecomAccountRegistry to retry registration.
Test: Manual; edited code in Subscriptionmanager to fail the first attempts
to add a listener for TelecomAccountRegistry. Verified the backoff took
place and registration still occurred for the listener.
Test: Tried to write a mockito test but gave up because this code has
far too many intertwined dependencies and is not inherently testable
Fixes: 152217039
Change-Id: I3f073aa11110dea7a6d32baaecae3889214a5552
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 79feb10..3b8f4fd 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -32,6 +32,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.UserHandle;
@@ -57,6 +58,7 @@
import android.text.TextUtils;
import com.android.ims.ImsManager;
+import com.android.internal.telephony.ExponentialBackoff;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SubscriptionController;
@@ -84,6 +86,26 @@
private final static int DEFAULT_SIM_ICON = R.drawable.ic_multi_sim;
private final static String GROUP_PREFIX = "group_";
+ private static final int REGISTER_START_DELAY_MS = 1 * 1000; // 1 second
+ private static final int REGISTER_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute
+
+ /**
+ * Indicates the {@link SubscriptionManager.OnSubscriptionsChangedListener} has not yet been
+ * registered.
+ */
+ private static final int LISTENER_STATE_UNREGISTERED = 0;
+
+ /**
+ * Indicates the first {@link SubscriptionManager.OnSubscriptionsChangedListener} registration
+ * attempt failed and we are performing backoff registration.
+ */
+ private static final int LISTENER_STATE_PERFORMING_BACKOFF = 2;
+
+ /**
+ * Indicates the {@link SubscriptionManager.OnSubscriptionsChangedListener} has been registered.
+ */
+ private static final int LISTENER_STATE_REGISTERED = 3;
+
final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
private final Phone mPhone;
private PhoneAccount mAccount;
@@ -951,18 +973,46 @@
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
- // Any time the SubscriptionInfo changes...rerun the setup
- Log.i(this, "onSubscriptionsChanged - update accounts");
+ if (mSubscriptionListenerState != LISTENER_STATE_REGISTERED) {
+ mRegisterSubscriptionListenerBackoff.stop();
+ mHandlerThread.quitSafely();
+ }
+ mSubscriptionListenerState = LISTENER_STATE_REGISTERED;
+
+ // Any time the SubscriptionInfo changes rerun the setup
+ Log.i(this, "TelecomAccountRegistry: onSubscriptionsChanged - update accounts");
tearDownAccounts();
setupAccounts();
}
+
+ @Override
+ public void onAddListenerFailed() {
+ // Woe! Failed to add the listener!
+ Log.w(this, "TelecomAccountRegistry: onAddListenerFailed - failed to register "
+ + "OnSubscriptionsChangedListener");
+
+ // Even though registering the listener failed, we will still try to setup the phone
+ // accounts now; the phone instances should already be present and ready, so even if
+ // telephony registry is poking along we can still try to setup the phone account.
+ tearDownAccounts();
+ setupAccounts();
+
+ if (mSubscriptionListenerState == LISTENER_STATE_UNREGISTERED) {
+ // Initial registration attempt failed; start exponential backoff.
+ mSubscriptionListenerState = LISTENER_STATE_PERFORMING_BACKOFF;
+ mRegisterSubscriptionListenerBackoff.start();
+ } else {
+ // We're already doing exponential backoff and a registration failed.
+ mRegisterSubscriptionListenerBackoff.notifyFailed();
+ }
+ }
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- Log.i(this, "User changed, re-registering phone accounts.");
+ Log.i(this, "TelecomAccountRegistry: User changed, re-registering phone accounts.");
UserHandle currentUser = intent.getParcelableExtra(Intent.EXTRA_USER);
mIsPrimaryUser = currentUser == null ? true : currentUser.isSystem();
@@ -1025,20 +1075,42 @@
private final SubscriptionManager mSubscriptionManager;
private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
private final Object mAccountsLock = new Object();
+ private int mSubscriptionListenerState = LISTENER_STATE_UNREGISTERED;
private int mServiceState = ServiceState.STATE_POWER_OFF;
private int mActiveDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mIsPrimaryUser = true;
+ private ExponentialBackoff mRegisterSubscriptionListenerBackoff;
+ private final HandlerThread mHandlerThread = new HandlerThread("TelecomAccountRegistry");
// TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
// pattern; redesign. This was added to fix a late release bug.
private TelephonyConnectionService mTelephonyConnectionService;
+ // Used to register subscription changed listener when initial attempts fail.
+ private Runnable mRegisterOnSubscriptionsChangedListenerRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mSubscriptionListenerState != LISTENER_STATE_REGISTERED) {
+ Log.i(this, "TelecomAccountRegistry: performing delayed register.");
+ SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
+ mOnSubscriptionsChangedListener);
+ }
+ }
+ };
+
TelecomAccountRegistry(Context context) {
mContext = context;
mTelecomManager = context.getSystemService(TelecomManager.class);
mImsManager = context.getSystemService(android.telephony.ims.ImsManager.class);
mTelephonyManager = TelephonyManager.from(context);
mSubscriptionManager = SubscriptionManager.from(context);
+ mHandlerThread.start();
+ mRegisterSubscriptionListenerBackoff = new ExponentialBackoff(
+ REGISTER_START_DELAY_MS,
+ REGISTER_MAXIMUM_DELAY_MS,
+ 2, /* multiplier */
+ mHandlerThread.getLooper(),
+ mRegisterOnSubscriptionsChangedListenerRunnable);
}
/**
@@ -1254,6 +1326,7 @@
// Register for SubscriptionInfo list changes which is guaranteed
// to invoke onSubscriptionsChanged the first time.
+ Log.i(this, "TelecomAccountRegistry: setupOnBoot - register subscription listener");
SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
mOnSubscriptionsChangedListener);