From feb16cb218d66b6f0ea84b8d8b8764be0db3222c Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Wed, 22 Feb 2023 13:13:25 -0800 Subject: [PATCH] Add executors to handle binder call. Use background executor to handle TelecomManager#IsInCall binder call and LockpatternUtils#isSecure which can sometimes lead to binder call. Also added a unit test file. Fixes: 266482108 Test: Unit test and interact with emergency button on bouncer. Observe its visibility. Change-Id: If590f39ebdc622a89d7de36becff973bf116e355 --- .../keyguard/EmergencyButtonController.java | 107 +++++++++++------ .../keyguard/EmergencyButtonControllerTest.kt | 112 ++++++++++++++++++ 2 files changed, 184 insertions(+), 35 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java index 62298f10d6b71..f7e8eb492584b 100644 --- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java +++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java @@ -18,6 +18,7 @@ package com.android.keyguard; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.annotation.SuppressLint; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.content.Intent; @@ -31,17 +32,22 @@ import android.telephony.TelephonyManager; import android.util.Log; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.dagger.KeyguardBouncerScope; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.util.EmergencyDialerConstants; import com.android.systemui.util.ViewController; +import java.util.concurrent.Executor; + import javax.inject.Inject; /** View Controller for {@link com.android.keyguard.EmergencyButton}. */ @@ -59,6 +65,8 @@ public class EmergencyButtonController extends ViewController { private EmergencyButtonCallback mEmergencyButtonCallback; private LockPatternUtils mLockPatternUtils; + private Executor mMainExecutor; + private Executor mBackgroundExecutor; private final KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @@ -80,13 +88,15 @@ public class EmergencyButtonController extends ViewController { } }; - private EmergencyButtonController(@Nullable EmergencyButton view, + @VisibleForTesting + public EmergencyButtonController(@Nullable EmergencyButton view, ConfigurationController configurationController, KeyguardUpdateMonitor keyguardUpdateMonitor, TelephonyManager telephonyManager, PowerManager powerManager, ActivityTaskManager activityTaskManager, ShadeController shadeController, @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, - LockPatternUtils lockPatternUtils) { + LockPatternUtils lockPatternUtils, + Executor mainExecutor, Executor backgroundExecutor) { super(view); mConfigurationController = configurationController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -97,6 +107,8 @@ public class EmergencyButtonController extends ViewController { mTelecomManager = telecomManager; mMetricsLogger = metricsLogger; mLockPatternUtils = lockPatternUtils; + mMainExecutor = mainExecutor; + mBackgroundExecutor = backgroundExecutor; } @Override @@ -117,15 +129,27 @@ public class EmergencyButtonController extends ViewController { mConfigurationController.removeCallback(mConfigurationListener); } - private void updateEmergencyCallButton() { + /** + * Updates the visibility of the emergency button. + * + * This method runs binder calls in a background thread. + */ + @VisibleForTesting + @SuppressLint("MissingPermission") + public void updateEmergencyCallButton() { if (mView != null) { - mView.updateEmergencyCallButton( - /* isInCall= */ mTelecomManager != null && mTelecomManager.isInCall(), - /* hasTelephonyRadio= */ getContext().getPackageManager().hasSystemFeature( - PackageManager.FEATURE_TELEPHONY), - /* simLocked= */ mKeyguardUpdateMonitor.isSimPinVoiceSecure(), - /* isSecure= */ - mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())); + // Run in bg thread to avoid throttling the main thread with binder call. + mBackgroundExecutor.execute(() -> { + boolean isInCall = mTelecomManager != null && mTelecomManager.isInCall(); + boolean isSecure = mLockPatternUtils + .isSecure(KeyguardUpdateMonitor.getCurrentUser()); + mMainExecutor.execute(() -> mView.updateEmergencyCallButton( + /* isInCall= */ isInCall, + /* hasTelephonyRadio= */ getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_TELEPHONY), + /* simLocked= */ mKeyguardUpdateMonitor.isSimPinVoiceSecure(), + /* isSecure= */ isSecure)); + }); } } @@ -135,6 +159,7 @@ public class EmergencyButtonController extends ViewController { /** * Shows the emergency dialer or returns the user to the existing call. */ + @SuppressLint("MissingPermission") public void takeEmergencyCallAction() { mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL); if (mPowerManager != null) { @@ -142,29 +167,35 @@ public class EmergencyButtonController extends ViewController { } mActivityTaskManager.stopSystemLockTaskMode(); mShadeController.collapseShade(false); - if (mTelecomManager != null && mTelecomManager.isInCall()) { - mTelecomManager.showInCallScreen(false); - if (mEmergencyButtonCallback != null) { - mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall(); - } - } else { - mKeyguardUpdateMonitor.reportEmergencyCallAction(true /* bypassHandler */); - if (mTelecomManager == null) { - Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer"); - return; - } - Intent emergencyDialIntent = - mTelecomManager.createLaunchEmergencyDialerIntent(null /* number*/) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - | Intent.FLAG_ACTIVITY_CLEAR_TOP) - .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, - EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON); + // Run in bg thread to avoid throttling the main thread with binder call. + mBackgroundExecutor.execute(() -> { + boolean isInCall = mTelecomManager != null && mTelecomManager.isInCall(); + mMainExecutor.execute(() -> { + if (isInCall) { + mTelecomManager.showInCallScreen(false); + if (mEmergencyButtonCallback != null) { + mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall(); + } + } else { + mKeyguardUpdateMonitor.reportEmergencyCallAction(true /* bypassHandler */); + if (mTelecomManager == null) { + Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer"); + return; + } + Intent emergencyDialIntent = + mTelecomManager.createLaunchEmergencyDialerIntent(null /* number*/) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_CLEAR_TOP) + .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, + EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON); - getContext().startActivityAsUser(emergencyDialIntent, - ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(), - new UserHandle(KeyguardUpdateMonitor.getCurrentUser())); - } + getContext().startActivityAsUser(emergencyDialIntent, + ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(), + new UserHandle(KeyguardUpdateMonitor.getCurrentUser())); + } + }); + }); } /** */ @@ -185,6 +216,8 @@ public class EmergencyButtonController extends ViewController { private final TelecomManager mTelecomManager; private final MetricsLogger mMetricsLogger; private final LockPatternUtils mLockPatternUtils; + private final Executor mMainExecutor; + private final Executor mBackgroundExecutor; @Inject public Factory(ConfigurationController configurationController, @@ -192,7 +225,9 @@ public class EmergencyButtonController extends ViewController { PowerManager powerManager, ActivityTaskManager activityTaskManager, ShadeController shadeController, @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, - LockPatternUtils lockPatternUtils) { + LockPatternUtils lockPatternUtils, + @Main Executor mainExecutor, + @Background Executor backgroundExecutor) { mConfigurationController = configurationController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -203,14 +238,16 @@ public class EmergencyButtonController extends ViewController { mTelecomManager = telecomManager; mMetricsLogger = metricsLogger; mLockPatternUtils = lockPatternUtils; + mMainExecutor = mainExecutor; + mBackgroundExecutor = backgroundExecutor; } /** Construct an {@link com.android.keyguard.EmergencyButtonController}. */ public EmergencyButtonController create(EmergencyButton view) { return new EmergencyButtonController(view, mConfigurationController, mKeyguardUpdateMonitor, mTelephonyManager, mPowerManager, mActivityTaskManager, - mShadeController, - mTelecomManager, mMetricsLogger, mLockPatternUtils); + mShadeController, mTelecomManager, mMetricsLogger, mLockPatternUtils, + mMainExecutor, mBackgroundExecutor); } } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt new file mode 100644 index 0000000000000..30fed0b0316fc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023 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.keyguard + +import android.app.ActivityTaskManager +import android.content.pm.PackageManager +import android.os.PowerManager +import android.telecom.TelecomManager +import android.telephony.TelephonyManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.internal.logging.MetricsLogger +import com.android.internal.widget.LockPatternUtils +import com.android.systemui.SysuiTestCase +import com.android.systemui.shade.ShadeController +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +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.Mockito +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class EmergencyButtonControllerTest : SysuiTestCase() { + lateinit var underTest: EmergencyButtonController + @Mock lateinit var emergencyButton: EmergencyButton + @Mock lateinit var configurationController: ConfigurationController + @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock lateinit var telephonyManager: TelephonyManager + @Mock lateinit var powerManager: PowerManager + @Mock lateinit var activityTaskManager: ActivityTaskManager + @Mock lateinit var shadeController: ShadeController + @Mock lateinit var telecomManager: TelecomManager + @Mock lateinit var metricsLogger: MetricsLogger + @Mock lateinit var lockPatternUtils: LockPatternUtils + @Mock lateinit var packageManager: PackageManager + val fakeSystemClock = FakeSystemClock() + val mainExecutor = FakeExecutor(fakeSystemClock) + val backgroundExecutor = FakeExecutor(fakeSystemClock) + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = + EmergencyButtonController( + emergencyButton, + configurationController, + keyguardUpdateMonitor, + telephonyManager, + powerManager, + activityTaskManager, + shadeController, + telecomManager, + metricsLogger, + lockPatternUtils, + mainExecutor, + backgroundExecutor + ) + context.setMockPackageManager(packageManager) + Mockito.`when`(emergencyButton.context).thenReturn(context) + } + + @Test + fun testUpdateEmergencyButton() { + Mockito.`when`(telecomManager.isInCall).thenReturn(true) + Mockito.`when`(lockPatternUtils.isSecure(anyInt())).thenReturn(true) + Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) + .thenReturn(true) + underTest.updateEmergencyCallButton() + backgroundExecutor.runAllReady() + verify(emergencyButton, never()) + .updateEmergencyCallButton( + /* isInCall= */ any(), + /* hasTelephonyRadio= */ any(), + /* simLocked= */ any(), + /* isSecure= */ any() + ) + mainExecutor.runAllReady() + verify(emergencyButton) + .updateEmergencyCallButton( + /* isInCall= */ eq(true), + /* hasTelephonyRadio= */ eq(true), + /* simLocked= */ any(), + /* isSecure= */ eq(true) + ) + } +}