diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierText.java b/packages/SystemUI/src/com/android/keyguard/CarrierText.java index f6b03c1fa0131..f1c044c0b5e6c 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierText.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierText.java @@ -18,6 +18,7 @@ package com.android.keyguard; import android.content.Context; import android.content.res.TypedArray; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.text.method.SingleLineTransformationMethod; import android.util.AttributeSet; @@ -26,6 +27,7 @@ import android.widget.TextView; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.telephony.TelephonyListenerManager; import java.util.Locale; @@ -85,7 +87,10 @@ public class CarrierText extends TextView { mSeparator = getResources().getString( com.android.internal.R.string.kg_text_message_separator); mCarrierTextController = new CarrierTextController(mContext, mSeparator, mShowAirplaneMode, - mShowMissingSim); + mShowMissingSim, mContext.getSystemService(TelephonyManager.class), + Dependency.get(TelephonyListenerManager.class), + Dependency.get(Dependency.MAIN_EXECUTOR), + Dependency.get(Dependency.BACKGROUND_EXECUTOR)); mShouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive(); setSelected(mShouldMarquee); // Allow marquee to work. } diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index d52a25139ce75..a85d96f133b1d 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -16,20 +16,15 @@ package com.android.keyguard; -import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; -import static android.telephony.PhoneStateListener.LISTEN_NONE; - import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.wifi.WifiManager; -import android.os.Handler; -import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -40,11 +35,14 @@ import androidx.annotation.VisibleForTesting; import com.android.settingslib.WirelessUtils; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.telephony.TelephonyListenerManager; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; @@ -59,8 +57,6 @@ public class CarrierTextController { private static final String TAG = "CarrierTextController"; private final boolean mIsEmergencyCallCapable; - private final Handler mMainHandler; - private final Handler mBgHandler; private boolean mTelephonyCapable; private boolean mShowMissingSim; private boolean mShowAirplaneMode; @@ -73,6 +69,10 @@ public class CarrierTextController { @Nullable // Check for nullability before dispatching private CarrierTextCallback mCarrierTextCallback; private Context mContext; + private final TelephonyManager mTelephonyManager; + private final TelephonyListenerManager mTelephonyListenerManager; + private final Executor mMainExecutor; + private final Executor mBgExecutor; private CharSequence mSeparator; private WakefulnessLifecycle mWakefulnessLifecycle; private final WakefulnessLifecycle.Observer mWakefulnessObserver = @@ -129,14 +129,13 @@ public class CarrierTextController { } }; - private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + private final ActiveDataSubscriptionIdListener mPhoneStateListener = + new ActiveDataSubscriptionIdListener() { @Override public void onActiveDataSubscriptionIdChanged(int subId) { - mActiveMobileDataSubscription = subId; - if (mNetworkSupported.get() && mCarrierTextCallback != null) { - updateCarrierText(); - } + if (mNetworkSupported.get() && mCarrierTextCallback != null) { + updateCarrierText(); + } } }; @@ -163,9 +162,15 @@ public class CarrierTextController { * @param separator Separator between different parts of the text */ public CarrierTextController(Context context, CharSequence separator, boolean showAirplaneMode, - boolean showMissingSim) { + boolean showMissingSim, TelephonyManager telephonyManager, + TelephonyListenerManager telephonyListenerManager, + @Main Executor mainExecutor, @Background Executor bgExecutor) { mContext = context; - mIsEmergencyCallCapable = getTelephonyManager().isVoiceCapable(); + mTelephonyManager = telephonyManager; + mTelephonyListenerManager = telephonyListenerManager; + mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; + mIsEmergencyCallCapable = mTelephonyManager.isVoiceCapable(); mShowAirplaneMode = showAirplaneMode; mShowMissingSim = showMissingSim; @@ -173,12 +178,10 @@ public class CarrierTextController { mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mSeparator = separator; mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); - mSimSlotsNumber = getTelephonyManager().getSupportedModemCount(); + mSimSlotsNumber = mTelephonyManager.getSupportedModemCount(); mSimErrorState = new boolean[mSimSlotsNumber]; - mMainHandler = Dependency.get(Dependency.MAIN_HANDLER); - mBgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - mBgHandler.post(() -> { + mBgExecutor.execute(() -> { boolean supported = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); if (supported && mNetworkSupported.compareAndSet(false, supported)) { @@ -208,7 +211,7 @@ public class CarrierTextController { CharSequence carrierTextForSimIOError = getCarrierTextForSimState( TelephonyManager.SIM_STATE_CARD_IO_ERROR, carrier); // mSimErrorState has the state of each sim indexed by slotID. - for (int index = 0; index < getTelephonyManager().getActiveModemCount(); index++) { + for (int index = 0; index < mTelephonyManager.getActiveModemCount(); index++) { if (!mSimErrorState[index]) { continue; } @@ -247,26 +250,24 @@ public class CarrierTextController { * This call will always be processed in a background thread. */ private void handleSetListening(CarrierTextCallback callback) { - TelephonyManager telephonyManager = getTelephonyManager(); if (callback != null) { mCarrierTextCallback = callback; if (mNetworkSupported.get()) { // Keyguard update monitor expects callbacks from main thread - mMainHandler.post(() -> mKeyguardUpdateMonitor.registerCallback(mCallback)); + mMainExecutor.execute(() -> mKeyguardUpdateMonitor.registerCallback(mCallback)); mWakefulnessLifecycle.addObserver(mWakefulnessObserver); - telephonyManager.listen(mPhoneStateListener, - LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener); } else { // Don't listen and clear out the text when the device isn't a phone. - mMainHandler.post(() -> callback.updateCarrierInfo( + mMainExecutor.execute(() -> callback.updateCarrierInfo( new CarrierTextCallbackInfo("", null, false, null) )); } } else { mCarrierTextCallback = null; - mMainHandler.post(() -> mKeyguardUpdateMonitor.removeCallback(mCallback)); + mMainExecutor.execute(() -> mKeyguardUpdateMonitor.removeCallback(mCallback)); mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); - telephonyManager.listen(mPhoneStateListener, LISTEN_NONE); + mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener); } } @@ -277,7 +278,7 @@ public class CarrierTextController { * @param callback Callback to provide text updates */ public void setListening(CarrierTextCallback callback) { - mBgHandler.post(() -> handleSetListening(callback)); + mBgExecutor.execute(() -> handleSetListening(callback)); } protected List getSubscriptionInfo() { @@ -400,7 +401,7 @@ public class CarrierTextController { protected void postToCallback(CarrierTextCallbackInfo info) { final CarrierTextCallback callback = mCarrierTextCallback; if (callback != null) { - mMainHandler.post(() -> callback.updateCarrierInfo(info)); + mMainExecutor.execute(() -> callback.updateCarrierInfo(info)); } } @@ -620,14 +621,25 @@ public class CarrierTextController { public static class Builder { private final Context mContext; private final String mSeparator; + private final TelephonyManager mTelephonyManager; + private final TelephonyListenerManager mTelephonyListenerManager; + private final Executor mMainExecutor; + private final Executor mBgExecutor; private boolean mShowAirplaneMode; private boolean mShowMissingSim; @Inject - public Builder(Context context, @Main Resources resources) { + public Builder(Context context, @Main Resources resources, + TelephonyManager telephonyManager, + TelephonyListenerManager telephonyListenerManager, @Main Executor mainExecutor, + @Background Executor bgExecutor) { mContext = context; mSeparator = resources.getString( com.android.internal.R.string.kg_text_message_separator); + mTelephonyManager = telephonyManager; + mTelephonyListenerManager = telephonyListenerManager; + mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; } @@ -643,7 +655,8 @@ public class CarrierTextController { public CarrierTextController build() { return new CarrierTextController( - mContext, mSeparator, mShowAirplaneMode, mShowMissingSim); + mContext, mSeparator, mShowAirplaneMode, mShowMissingSim, mTelephonyManager, + mTelephonyListenerManager, mMainExecutor, mBgExecutor); } } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 69e6ed043172d..9abc1e7adf864 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -22,7 +22,6 @@ import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.ACTION_USER_STOPPED; import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; -import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; @@ -76,11 +75,11 @@ import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.telephony.CarrierConfigManager; -import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.Log; import android.util.SparseArray; @@ -107,6 +106,7 @@ import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.Assert; import com.android.systemui.util.RingerModeTracker; @@ -290,6 +290,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean mDeviceInteractive; private boolean mScreenOn; private SubscriptionManager mSubscriptionManager; + private final TelephonyListenerManager mTelephonyListenerManager; private List mSubscriptionInfo; private TrustManager mTrustManager; private UserManager mUserManager; @@ -358,7 +359,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab }; @VisibleForTesting - public PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + public TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener = + new TelephonyCallback.ActiveDataSubscriptionIdListener() { @Override public void onActiveDataSubscriptionIdChanged(int subId) { mActiveMobileDataSubscription = subId; @@ -1614,9 +1616,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab StatusBarStateController statusBarStateController, LockPatternUtils lockPatternUtils, AuthController authController, + TelephonyListenerManager telephonyListenerManager, FeatureFlags featureFlags) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); + mTelephonyListenerManager = telephonyListenerManager; mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged); mBackgroundExecutor = backgroundExecutor; @@ -1865,8 +1869,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (mTelephonyManager != null) { - mTelephonyManager.listen(mPhoneStateListener, - LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener); // Set initial sim states values. for (int slot = 0; slot < mTelephonyManager.getActiveModemCount(); slot++) { int state = mTelephonyManager.getSimState(slot); @@ -3123,7 +3126,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); if (telephony != null) { - telephony.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); + mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener); } mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener); diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 06b486ec43d0b..a686fc086b401 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -120,6 +120,7 @@ import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tuner.TunablePadding.TunablePaddingService; import com.android.systemui.tuner.TunerService; @@ -350,6 +351,7 @@ public class Dependency { @Inject Lazy mMediaOutputDialogFactory; @Inject Lazy mDeviceConfigProxy; @Inject Lazy mNavbarButtonsControllerLazy; + @Inject Lazy mTelephonyListenerManager; @Inject public Dependency() { @@ -545,6 +547,7 @@ public class Dependency { mProviders.put(StatusBar.class, mStatusBar::get); mProviders.put(ProtoTracer.class, mProtoTracer::get); mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get); + mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get); // TODO(b/118592525): to support multi-display , we start to add something which is // per-display, while others may be global. I think it's time to add diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 553e5a7c6d7dd..18627189f1884 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -73,8 +73,8 @@ import android.provider.Settings; import android.service.dreams.IDreamManager; import android.sysprop.TelephonyProperties; import android.telecom.TelecomManager; -import android.telephony.PhoneStateListener; import android.telephony.ServiceState; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.transition.AutoTransition; import android.transition.TransitionManager; @@ -138,6 +138,7 @@ import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.EmergencyDialerConstants; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.leak.RotationUtils; @@ -303,7 +304,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, AudioManager audioManager, IDreamManager iDreamManager, DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils, BroadcastDispatcher broadcastDispatcher, - ConnectivityManager connectivityManager, TelephonyManager telephonyManager, + ConnectivityManager connectivityManager, + TelephonyListenerManager telephonyListenerManager, ContentResolver contentResolver, @Nullable Vibrator vibrator, @Main Resources resources, ConfigurationController configurationController, ActivityStarter activityStarter, KeyguardStateController keyguardStateController, UserManager userManager, @@ -361,7 +363,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); // get notified of phone state changes - telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE); + telephonyListenerManager.addServiceStateListener(mPhoneStateListener); contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, mAirplaneModeObserver); @@ -2049,7 +2051,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } }; - PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + private final TelephonyCallback.ServiceStateListener mPhoneStateListener = + new TelephonyCallback.ServiceStateListener() { @Override public void onServiceStateChanged(ServiceState serviceState) { if (!mHasTelephony) return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 8a86021423631..cfaeb0edec534 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -22,7 +22,6 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT; import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE; import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT; -import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; import android.annotation.Nullable; import android.content.BroadcastReceiver; @@ -44,11 +43,11 @@ import android.os.Looper; import android.provider.Settings; import android.telephony.CarrierConfigManager; import android.telephony.CellSignalStrength; -import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.FeatureFlagUtils; @@ -74,6 +73,7 @@ import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import com.android.systemui.telephony.TelephonyListenerManager; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -108,6 +108,7 @@ public class NetworkControllerImpl extends BroadcastReceiver private final Context mContext; private final TelephonyManager mPhone; + private final TelephonyListenerManager mTelephonyListenerManager; private final WifiManager mWifiManager; private final ConnectivityManager mConnectivityManager; private final SubscriptionManager mSubscriptionManager; @@ -121,7 +122,7 @@ public class NetworkControllerImpl extends BroadcastReceiver private final boolean mProviderModel; private Config mConfig; - private PhoneStateListener mPhoneStateListener; + private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; // Subcontrollers. @@ -201,12 +202,14 @@ public class NetworkControllerImpl extends BroadcastReceiver BroadcastDispatcher broadcastDispatcher, ConnectivityManager connectivityManager, TelephonyManager telephonyManager, + TelephonyListenerManager telephonyListenerManager, @Nullable WifiManager wifiManager, NetworkScoreManager networkScoreManager, AccessPointControllerImpl accessPointController, DemoModeController demoModeController) { this(context, connectivityManager, telephonyManager, + telephonyListenerManager, wifiManager, networkScoreManager, SubscriptionManager.from(context), Config.readConfig(context), bgLooper, @@ -222,7 +225,9 @@ public class NetworkControllerImpl extends BroadcastReceiver @VisibleForTesting NetworkControllerImpl(Context context, ConnectivityManager connectivityManager, - TelephonyManager telephonyManager, WifiManager wifiManager, + TelephonyManager telephonyManager, + TelephonyListenerManager telephonyListenerManager, + WifiManager wifiManager, NetworkScoreManager networkScoreManager, SubscriptionManager subManager, Config config, Looper bgLooper, CallbackHandler callbackHandler, @@ -233,6 +238,7 @@ public class NetworkControllerImpl extends BroadcastReceiver BroadcastDispatcher broadcastDispatcher, DemoModeController demoModeController) { mContext = context; + mTelephonyListenerManager = telephonyListenerManager; mConfig = config; mReceiverHandler = new Handler(bgLooper); mCallbackHandler = callbackHandler; @@ -372,23 +378,20 @@ public class NetworkControllerImpl extends BroadcastReceiver // exclusively for status bar icons. mConnectivityManager.registerDefaultNetworkCallback(callback, mReceiverHandler); // Register the listener on our bg looper - mPhoneStateListener = new PhoneStateListener(mReceiverHandler::post) { - @Override - public void onActiveDataSubscriptionIdChanged(int subId) { - // For data switching from A to B, we assume B is validated for up to 2 seconds iff: - // 1) A and B are in the same subscription group e.g. CBRS data switch. And - // 2) A was validated before the switch. - // This is to provide smooth transition for UI without showing cross during data - // switch. - if (keepCellularValidationBitInSwitch(mActiveMobileDataSubscription, subId)) { - if (DEBUG) Log.d(TAG, ": mForceCellularValidated to true."); - mForceCellularValidated = true; - mReceiverHandler.removeCallbacks(mClearForceValidated); - mReceiverHandler.postDelayed(mClearForceValidated, 2000); - } - mActiveMobileDataSubscription = subId; - doUpdateMobileControllers(); + mPhoneStateListener = subId -> { + // For data switching from A to B, we assume B is validated for up to 2 seconds iff: + // 1) A and B are in the same subscription group e.g. CBRS data switch. And + // 2) A was validated before the switch. + // This is to provide smooth transition for UI without showing cross during data + // switch. + if (keepCellularValidationBitInSwitch(mActiveMobileDataSubscription, subId)) { + if (DEBUG) Log.d(TAG, ": mForceCellularValidated to true."); + mForceCellularValidated = true; + mReceiverHandler.removeCallbacks(mClearForceValidated); + mReceiverHandler.postDelayed(mClearForceValidated, 2000); } + mActiveMobileDataSubscription = subId; + doUpdateMobileControllers(); }; mDemoModeController.addCallback(this); @@ -428,7 +431,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mSubscriptionListener = new SubListener(); } mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener); - mPhone.listen(mPhoneStateListener, LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener); // broadcasts IntentFilter filter = new IntentFilter(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index d4029e64036ed..d05d972a50e6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -42,8 +42,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; +import android.telephony.TelephonyCallback; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -69,6 +68,7 @@ import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.tiles.UserDetailView; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.CreateUserActivity; import java.io.FileDescriptor; @@ -105,6 +105,7 @@ public class UserSwitcherController implements Dumpable { protected final Handler mHandler; private final ActivityStarter mActivityStarter; private final BroadcastDispatcher mBroadcastDispatcher; + private final TelephonyListenerManager mTelephonyListenerManager; private final IActivityTaskManager mActivityTaskManager; private ArrayList mUsers = new ArrayList<>(); @@ -127,9 +128,11 @@ public class UserSwitcherController implements Dumpable { public UserSwitcherController(Context context, KeyguardStateController keyguardStateController, @Main Handler handler, ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger, + TelephonyListenerManager telephonyListenerManager, IActivityTaskManager activityTaskManager) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; + mTelephonyListenerManager = telephonyListenerManager; mActivityTaskManager = activityTaskManager; mUiEventLogger = uiEventLogger; mUserDetailAdapter = new UserDetailAdapter(this, mContext, mUiEventLogger); @@ -458,18 +461,15 @@ public class UserSwitcherController implements Dumpable { } private void listenForCallState() { - final TelephonyManager tele = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - if (tele != null) { - tele.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); - } + mTelephonyListenerManager.addCallStateListener(mPhoneStateListener); } - private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + private final TelephonyCallback.CallStateListener mPhoneStateListener = + new TelephonyCallback.CallStateListener() { private int mCallState; @Override - public void onCallStateChanged(int state, String incomingNumber) { + public void onCallStateChanged(int state) { if (mCallState == state) return; if (DEBUG) Log.v(TAG, "Call state changed: " + state); mCallState = state; diff --git a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java new file mode 100644 index 0000000000000..3bc26322e6cd1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.telephony; + +import android.telephony.ServiceState; +import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; +import android.telephony.TelephonyCallback.CallStateListener; +import android.telephony.TelephonyCallback.ServiceStateListener; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +class TelephonyCallback extends android.telephony.TelephonyCallback + implements ActiveDataSubscriptionIdListener, CallStateListener, ServiceStateListener { + + private final List mActiveDataSubscriptionIdListeners = + new ArrayList<>(); + private final List mCallStateListeners = new ArrayList<>(); + private final List mServiceStateListeners = new ArrayList<>(); + + @Inject + TelephonyCallback() { + } + + boolean hasAnyListeners() { + return !mActiveDataSubscriptionIdListeners.isEmpty() + || !mCallStateListeners.isEmpty() + || !mServiceStateListeners.isEmpty(); + } + + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mActiveDataSubscriptionIdListeners.forEach(listener -> { + listener.onActiveDataSubscriptionIdChanged(subId); + }); + } + + void addActiveDataSubscriptionIdListener(ActiveDataSubscriptionIdListener listener) { + mActiveDataSubscriptionIdListeners.add(listener); + } + + void removeActiveDataSubscriptionIdListener(ActiveDataSubscriptionIdListener listener) { + mActiveDataSubscriptionIdListeners.remove(listener); + } + + @Override + public void onCallStateChanged(int state) { + mCallStateListeners.forEach(listener -> { + listener.onCallStateChanged(state); + }); + } + + void addCallStateListener(CallStateListener listener) { + mCallStateListeners.add(listener); + } + + void removeCallStateListener(CallStateListener listener) { + mCallStateListeners.remove(listener); + } + + @Override + public void onServiceStateChanged(@NonNull ServiceState serviceState) { + mServiceStateListeners.forEach(listener -> { + listener.onServiceStateChanged(serviceState); + }); + } + + void addServiceStateListener(ServiceStateListener listener) { + mServiceStateListeners.add(listener); + } + + void removeServiceStateListener(ServiceStateListener listener) { + mServiceStateListeners.remove(listener); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java new file mode 100644 index 0000000000000..4e1accacfb7f1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.telephony; + +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; +import android.telephony.TelephonyCallback.CallStateListener; +import android.telephony.TelephonyCallback.ServiceStateListener; +import android.telephony.TelephonyManager; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +/** + * Wrapper around {@link TelephonyManager#listen(PhoneStateListener, int)}. + * + * The TelephonyManager complains if too many places in code register a listener. This class + * encapsulates SystemUI's usage of this function, reducing it down to a single listener. + * + * See also + * {@link TelephonyManager#registerTelephonyCallback(Executor, android.telephony.TelephonyCallback)} + */ +@SysUISingleton +public class TelephonyListenerManager { + private final TelephonyManager mTelephonyManager; + private final Executor mExecutor; + private final TelephonyCallback mTelephonyCallback; + + private boolean mListening = false; + + @Inject + public TelephonyListenerManager(TelephonyManager telephonyManager, @Main Executor executor, + TelephonyCallback telephonyCallback) { + mTelephonyManager = telephonyManager; + mExecutor = executor; + mTelephonyCallback = telephonyCallback; + } + + /** */ + public void addActiveDataSubscriptionIdListener(ActiveDataSubscriptionIdListener listener) { + mTelephonyCallback.addActiveDataSubscriptionIdListener(listener); + updateListening(); + } + + /** */ + public void removeActiveDataSubscriptionIdListener(ActiveDataSubscriptionIdListener listener) { + mTelephonyCallback.removeActiveDataSubscriptionIdListener(listener); + updateListening(); + } + + /** */ + public void addCallStateListener(CallStateListener listener) { + mTelephonyCallback.addCallStateListener(listener); + updateListening(); + } + + /** */ + public void removeCallStateListener(CallStateListener listener) { + mTelephonyCallback.removeCallStateListener(listener); + updateListening(); + } + + /** */ + public void addServiceStateListener(ServiceStateListener listener) { + mTelephonyCallback.addServiceStateListener(listener); + updateListening(); + } + + /** */ + public void removeServiceStateListener(ServiceStateListener listener) { + mTelephonyCallback.removeServiceStateListener(listener); + updateListening(); + } + + + private void updateListening() { + if (!mListening && mTelephonyCallback.hasAnyListeners()) { + mListening = true; + mTelephonyManager.registerTelephonyCallback(mExecutor, mTelephonyCallback); + } else if (mListening && !mTelephonyCallback.hasAnyListeners()) { + mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback); + mListening = false; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index 6f2c0af5384df..c54620248ceb3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -21,12 +21,13 @@ import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE; import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -40,10 +41,6 @@ import static org.mockito.Mockito.when; import android.content.pm.PackageManager; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Process; import android.provider.Settings; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; @@ -51,13 +48,14 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import android.text.TextUtils; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.telephony.TelephonyListenerManager; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -73,7 +71,6 @@ import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class CarrierTextControllerTest extends SysuiTestCase { private static final CharSequence SEPARATOR = " \u2014 "; @@ -103,24 +100,25 @@ public class CarrierTextControllerTest extends SysuiTestCase { @Mock private TelephonyManager mTelephonyManager; @Mock + private TelephonyListenerManager mTelephonyListenerManager; + private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); + private FakeExecutor mBgExecutor = new FakeExecutor(mFakeSystemClock); + @Mock private SubscriptionManager mSubscriptionManager; private CarrierTextController.CarrierTextCallbackInfo mCarrierTextCallbackInfo; private CarrierTextController mCarrierTextController; - private TestableLooper mTestableLooper; private Void checkMainThread(InvocationOnMock inv) { - Looper mainLooper = Dependency.get(Dependency.MAIN_HANDLER).getLooper(); - if (!mainLooper.isCurrentThread()) { - fail("This call should be done from the main thread"); - } + assertThat(mMainExecutor.isExecuting()).isTrue(); + assertThat(mBgExecutor.isExecuting()).isFalse(); return null; } @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTestableLooper = TestableLooper.get(this); mContext.addMockSystemService(WifiManager.class, mWifiManager); mContext.addMockSystemService(PackageManager.class, mPackageManager); @@ -132,9 +130,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { mContext.getOrCreateTestableResources().addOverride( R.string.airplane_mode, AIRPLANE_MODE_TEXT); mDependency.injectMockDependency(WakefulnessLifecycle.class); - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, - new Handler(mTestableLooper.getLooper())); - mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor); doAnswer(this::checkMainThread).when(mKeyguardUpdateMonitor) @@ -147,30 +142,21 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mTelephonyManager.getSupportedModemCount()).thenReturn(3); when(mTelephonyManager.getActiveModemCount()).thenReturn(3); - mCarrierTextController = new CarrierTextController(mContext, SEPARATOR, true, true); + mCarrierTextController = new CarrierTextController( + mContext, SEPARATOR, true, true, + mTelephonyManager, mTelephonyListenerManager, mMainExecutor, + mBgExecutor); // This should not start listening on any of the real dependencies but will test that // callbacks in mKeyguardUpdateMonitor are done in the mTestableLooper thread mCarrierTextController.setListening(mCarrierTextCallback); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); } @Test public void testKeyguardUpdateMonitorCalledInMainThread() throws Exception { - // This test will run on the main looper (which is not the same as the looper set as MAIN - // for CarrierTextCallback. This will fail if calls to mKeyguardUpdateMonitor are not done - // through the looper set in the set up - HandlerThread thread = new HandlerThread("testThread", - Process.THREAD_PRIORITY_BACKGROUND); - thread.start(); - TestableLooper testableLooper = new TestableLooper(thread.getLooper()); - Handler h = new Handler(testableLooper.getLooper()); - h.post(() -> { - mCarrierTextController.setListening(null); - mCarrierTextController.setListening(mCarrierTextCallback); - }); - testableLooper.processAllMessages(); - mTestableLooper.processAllMessages(); - thread.quitSafely(); + mCarrierTextController.setListening(null); + mCarrierTextController.setListening(mCarrierTextCallback); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); } @Test @@ -189,7 +175,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { ArgumentCaptor.forClass( CarrierTextController.CarrierTextCallbackInfo.class); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText); } @@ -212,7 +198,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { ArgumentCaptor.forClass( CarrierTextController.CarrierTextCallbackInfo.class); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals("TEST_CARRIER" + SEPARATOR + INVALID_CARD_TEXT, captor.getValue().carrierText); // There's only one subscription in the list @@ -224,7 +210,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mTelephonyManager.getActiveModemCount()).thenReturn(1); // Update carrier text. It should ignore error state of subId 3 in inactive slotId. mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals("TEST_CARRIER", captor.getValue().carrierText); } @@ -260,7 +246,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { mCarrierTextController.mCallback.onSimStateChanged(0, 1, TelephonyManager.SIM_STATE_CARD_IO_ERROR); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo( any(CarrierTextController.CarrierTextCallbackInfo.class)); } @@ -269,7 +255,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { public void testCallback() { reset(mCarrierTextCallback); mCarrierTextController.postToCallback(mCarrierTextCallbackInfo); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); ArgumentCaptor captor = ArgumentCaptor.forClass( @@ -286,7 +272,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { mCarrierTextController.setListening(null); // This shouldn't produce NPE - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(any()); } @@ -306,7 +292,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); @@ -331,7 +317,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); @@ -356,7 +342,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertTrue("Carrier text should be empty, instead it's " + captor.getValue().carrierText, @@ -385,7 +371,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertFalse("No SIM should be available", captor.getValue().anySimReady); @@ -412,7 +398,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); @@ -438,7 +424,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER, @@ -463,7 +449,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals(TEST_CARRIER, @@ -488,7 +474,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals(TEST_CARRIER, @@ -514,7 +500,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { CarrierTextController.CarrierTextCallbackInfo.class); mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 52e2016d6f0ed..160dae5799644 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -88,6 +88,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.RingerModeTracker; import org.junit.After; @@ -163,6 +164,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Mock private AuthController mAuthController; @Mock + private TelephonyListenerManager mTelephonyListenerManager; + @Mock private FeatureFlags mFeatureFlags; @Captor private ArgumentCaptor mStatusBarStateListenerCaptor; @@ -883,7 +886,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mBroadcastDispatcher, mDumpManager, mRingerModeTracker, mBackgroundExecutor, mStatusBarStateController, mLockPatternUtils, - mAuthController, mFeatureFlags); + mAuthController, mTelephonyListenerManager, mFeatureFlags); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index eedf09936b4d0..8add93032caa5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -43,7 +43,6 @@ import android.os.Handler; import android.os.RemoteException; import android.os.UserManager; import android.service.dreams.IDreamManager; -import android.telephony.TelephonyManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.IWindowManager; @@ -75,6 +74,7 @@ import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.settings.SecureSettings; @@ -106,7 +106,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock private LockPatternUtils mLockPatternUtils; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private ConnectivityManager mConnectivityManager; - @Mock private TelephonyManager mTelephonyManager; + @Mock private TelephonyListenerManager mTelephonyListenerManager; @Mock private ContentResolver mContentResolver; @Mock private Resources mResources; @Mock private ConfigurationController mConfigurationController; @@ -167,7 +167,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mLockPatternUtils, mBroadcastDispatcher, mConnectivityManager, - mTelephonyManager, + mTelephonyListenerManager, mContentResolver, null, mResources, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 8f36415d60afa..ef3317288e4ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -76,6 +76,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.telephony.TelephonyListenerManager; import org.junit.After; import org.junit.Before; @@ -113,6 +114,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected NetworkScoreManager mMockNsm; protected SubscriptionManager mMockSm; protected TelephonyManager mMockTm; + protected TelephonyListenerManager mTelephonyListenerManager; protected BroadcastDispatcher mMockBd; protected Config mConfig; protected CallbackHandler mCallbackHandler; @@ -164,6 +166,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mDemoModeController = mock(DemoModeController.class); mMockWm = mock(WifiManager.class); mMockTm = mock(TelephonyManager.class); + mTelephonyListenerManager = mock(TelephonyListenerManager.class); mMockSm = mock(SubscriptionManager.class); mMockCm = mock(ConnectivityManager.class); mMockBd = mock(BroadcastDispatcher.class); @@ -213,6 +216,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, + mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, @@ -285,7 +289,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected NetworkControllerImpl setUpNoMobileData() { when(mMockTm.isDataCapable()).thenReturn(false); NetworkControllerImpl networkControllerNoMobile = - new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockNsm, mMockSm, + new NetworkControllerImpl(mContext, mMockCm, mMockTm, mTelephonyListenerManager, + mMockWm, mMockNsm, mMockSm, mConfig, TestableLooper.get(this).getLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), mMockSubDefaults, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index b108dd817bde5..f4ad819acf571 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -106,7 +106,8 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { public void test4gDataIcon() { // Switch to showing 4g icon and re-initialize the NetworkController. mConfig.show4gForLte = true; - mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, + mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, + mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), mMockSubDefaults, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index 91e9f0622cbf8..3c5cbb69eef6a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java @@ -61,8 +61,9 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { // Turn off mobile network support. when(mMockTm.isDataCapable()).thenReturn(false); // Create a new NetworkController as this is currently handled in constructor. - mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, - mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler, + mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, + mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig, + Looper.getMainLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd, mDemoModeController); @@ -80,8 +81,9 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { when(mMockTm.getServiceState()).thenReturn(mServiceState); when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(Collections.emptyList()); - mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, - mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler, + mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, + mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig, + Looper.getMainLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd, mDemoModeController); @@ -147,8 +149,9 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { // Turn off mobile network support. when(mMockTm.isDataCapable()).thenReturn(false); // Create a new NetworkController as this is currently handled in constructor. - mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, - mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler, + mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, + mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig, + Looper.getMainLooper(), mCallbackHandler, mock(AccessPointControllerImpl.class), mock(DataUsageController.class), mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd, mDemoModeController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java new file mode 100644 index 0000000000000..463b33602c150 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.telephony; + +import static com.google.common.truth.Truth.assertThat; + +import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; +import android.telephony.TelephonyCallback.CallStateListener; +import android.telephony.TelephonyCallback.ServiceStateListener; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TelephonyCallbackTest extends SysuiTestCase { + + private TelephonyCallback mTelephonyCallback = new TelephonyCallback(); + + @Test + public void testAddListener_ActiveDataSubscriptionIdListener() { + assertThat(mTelephonyCallback.hasAnyListeners()).isFalse(); + mTelephonyCallback.addActiveDataSubscriptionIdListener(subId -> {}); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.addActiveDataSubscriptionIdListener(subId -> {}); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + } + + @Test + public void testAddListener_CallStateListener() { + assertThat(mTelephonyCallback.hasAnyListeners()).isFalse(); + mTelephonyCallback.addCallStateListener(state -> {}); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.addCallStateListener(state -> {}); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + } + + @Test + public void testAddListener_ServiceStateListener() { + assertThat(mTelephonyCallback.hasAnyListeners()).isFalse(); + mTelephonyCallback.addServiceStateListener(serviceState -> {}); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.addServiceStateListener(serviceState -> {}); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + } + + @Test + public void testRemoveListener_ActiveDataSubscriptionIdListener() { + ActiveDataSubscriptionIdListener listener = subId -> {}; + mTelephonyCallback.addActiveDataSubscriptionIdListener(listener); + mTelephonyCallback.addActiveDataSubscriptionIdListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.removeActiveDataSubscriptionIdListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.removeActiveDataSubscriptionIdListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isFalse(); + } + + @Test + public void testRemoveListener_CallStateListener() { + CallStateListener listener = state -> {}; + mTelephonyCallback.addCallStateListener(listener); + mTelephonyCallback.addCallStateListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.removeCallStateListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.removeCallStateListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isFalse(); + } + + @Test + public void testRemoveListener_ServiceStateListener() { + ServiceStateListener listener = serviceState -> {}; + mTelephonyCallback.addServiceStateListener(listener); + mTelephonyCallback.addServiceStateListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.removeServiceStateListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isTrue(); + mTelephonyCallback.removeServiceStateListener(listener); + assertThat(mTelephonyCallback.hasAnyListeners()).isFalse(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java new file mode 100644 index 0000000000000..0d1ac7b96f0e8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.telephony; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; +import android.telephony.TelephonyCallback.CallStateListener; +import android.telephony.TelephonyCallback.ServiceStateListener; +import android.telephony.TelephonyManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TelephonyListenerManagerTest extends SysuiTestCase { + + @Mock + private TelephonyManager mTelephonyManager; + private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + @Mock + private TelephonyCallback mTelephonyCallback; + + TelephonyListenerManager mTelephonyListenerManager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mTelephonyListenerManager = new TelephonyListenerManager( + mTelephonyManager, mExecutor, mTelephonyCallback); + } + + @Test + public void testAddListenerRegisters_ActiveDataSubscriptionIdListener() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(subId -> {}); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(subId -> {}); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(subId -> {}); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(subId -> {}); + + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + } + + @Test + public void testAddListenerRegisters_CallStateListener() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + mTelephonyListenerManager.addCallStateListener(state -> {}); + mTelephonyListenerManager.addCallStateListener(state -> {}); + mTelephonyListenerManager.addCallStateListener(state -> {}); + mTelephonyListenerManager.addCallStateListener(state -> {}); + + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + } + + @Test + public void testAddListenerRegisters_ServiceStateListener() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + mTelephonyListenerManager.addServiceStateListener(serviceState -> {}); + mTelephonyListenerManager.addServiceStateListener(serviceState -> {}); + mTelephonyListenerManager.addServiceStateListener(serviceState -> {}); + mTelephonyListenerManager.addServiceStateListener(serviceState -> {}); + + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + } + + @Test + public void testAddListenerRegisters_mixed() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(subId -> {}); + mTelephonyListenerManager.addCallStateListener(state -> {}); + mTelephonyListenerManager.addServiceStateListener(serviceState -> {}); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(subId -> {}); + mTelephonyListenerManager.addCallStateListener(state -> {}); + mTelephonyListenerManager.addServiceStateListener(serviceState -> {}); + + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + } + + @Test + public void testRemoveListenerUnregisters_ActiveDataSubscriptionIdListener() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + ActiveDataSubscriptionIdListener mListener = subId -> { }; + + // Need to add one to actually register + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mListener); + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + reset(mTelephonyManager); + + when(mTelephonyCallback.hasAnyListeners()).thenReturn(false); + mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mListener); + mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mListener); + mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mListener); + mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mListener); + verify(mTelephonyManager, times(1)) + .unregisterTelephonyCallback(mTelephonyCallback); + } + + @Test + public void testRemoveListenerUnregisters_CallStateListener() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + CallStateListener mListener = state -> { }; + + // Need to add one to actually register + mTelephonyListenerManager.addCallStateListener(mListener); + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + reset(mTelephonyManager); + + when(mTelephonyCallback.hasAnyListeners()).thenReturn(false); + mTelephonyListenerManager.removeCallStateListener(mListener); + mTelephonyListenerManager.removeCallStateListener(mListener); + mTelephonyListenerManager.removeCallStateListener(mListener); + mTelephonyListenerManager.removeCallStateListener(mListener); + verify(mTelephonyManager, times(1)) + .unregisterTelephonyCallback(mTelephonyCallback); + } + + @Test + public void testRemoveListenerUnregisters_ServiceStateListener() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + ServiceStateListener mListener = serviceState -> { }; + + // Need to add one to actually register + mTelephonyListenerManager.addServiceStateListener(mListener); + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + reset(mTelephonyManager); + + when(mTelephonyCallback.hasAnyListeners()).thenReturn(false); + mTelephonyListenerManager.removeServiceStateListener(mListener); + mTelephonyListenerManager.removeServiceStateListener(mListener); + mTelephonyListenerManager.removeServiceStateListener(mListener); + mTelephonyListenerManager.removeServiceStateListener(mListener); + verify(mTelephonyManager, times(1)) + .unregisterTelephonyCallback(mTelephonyCallback); + } + + @Test + public void testRemoveListenerUnregisters_mixed() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + ActiveDataSubscriptionIdListener mListenerA = subId -> { }; + ServiceStateListener mListenerB = serviceState -> { }; + CallStateListener mListenerC = state -> { }; + + // Need to add one to actually register + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mListenerA); + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + reset(mTelephonyManager); + + when(mTelephonyCallback.hasAnyListeners()).thenReturn(false); + mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mListenerA); + mTelephonyListenerManager.removeServiceStateListener(mListenerB); + mTelephonyListenerManager.removeCallStateListener(mListenerC); + mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mListenerA); + mTelephonyListenerManager.removeServiceStateListener(mListenerB); + mTelephonyListenerManager.removeCallStateListener(mListenerC); + verify(mTelephonyManager, times(1)) + .unregisterTelephonyCallback(mTelephonyCallback); + } + + @Test + public void testAddListener_noDoubleRegister() { + when(mTelephonyCallback.hasAnyListeners()).thenReturn(true); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(subId -> {}); + verify(mTelephonyManager, times(1)) + .registerTelephonyCallback(mExecutor, mTelephonyCallback); + + reset(mTelephonyManager); + + // A second call to add doesn't register another listener. + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(subId -> {}); + verify(mTelephonyManager, never()).registerTelephonyCallback(mExecutor, mTelephonyCallback); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java index 7c7ad5322d48d..d3d30f242dcff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java @@ -27,6 +27,7 @@ public class FakeExecutor implements DelayableExecutor { private final FakeSystemClock mClock; private PriorityQueue mQueuedRunnables = new PriorityQueue<>(); private boolean mIgnoreClockUpdates; + private boolean mExecuting; /** * Initializes a fake executor. @@ -56,7 +57,9 @@ public class FakeExecutor implements DelayableExecutor { */ public boolean runNextReady() { if (!mQueuedRunnables.isEmpty() && mQueuedRunnables.peek().mWhen <= mClock.uptimeMillis()) { + mExecuting = true; mQueuedRunnables.poll().mRunnable.run(); + mExecuting = false; return true; } @@ -162,6 +165,10 @@ public class FakeExecutor implements DelayableExecutor { executeDelayed(command, 0); } + public boolean isExecuting() { + return mExecuting; + } + /** * Run all Executors in a loop until they all report they have no ready work to do. * diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java index abc283f40b1c2..87206c5b1c80d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java @@ -16,6 +16,8 @@ package com.android.systemui.util.concurrency; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -319,6 +321,18 @@ public class FakeExecutorTest extends SysuiTestCase { assertEquals(1, runnable.mRunCount); } + @Test + public void testIsExecuting() { + FakeSystemClock clock = new FakeSystemClock(); + FakeExecutor fakeExecutor = new FakeExecutor(clock); + + Runnable runnable = () -> assertThat(fakeExecutor.isExecuting()).isTrue(); + + assertThat(fakeExecutor.isExecuting()).isFalse(); + fakeExecutor.execute(runnable); + assertThat(fakeExecutor.isExecuting()).isFalse(); + } + private static class RunnableImpl implements Runnable { int mRunCount;