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
This commit is contained in:
Aaron Liu
2023-02-22 13:13:25 -08:00
parent e0aff5f686
commit feb16cb218
2 changed files with 184 additions and 35 deletions

View File

@@ -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<EmergencyButton> {
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<EmergencyButton> {
}
};
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<EmergencyButton> {
mTelecomManager = telecomManager;
mMetricsLogger = metricsLogger;
mLockPatternUtils = lockPatternUtils;
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
}
@Override
@@ -117,15 +129,27 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
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<EmergencyButton> {
/**
* 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<EmergencyButton> {
}
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<EmergencyButton> {
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<EmergencyButton> {
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<EmergencyButton> {
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);
}
}
}

View File

@@ -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)
)
}
}