1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.keyguard; 18 19 import static com.android.systemui.DejankUtils.whitelistIpcs; 20 21 import android.annotation.SuppressLint; 22 import android.app.ActivityOptions; 23 import android.app.ActivityTaskManager; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.content.res.Configuration; 27 import android.os.PowerManager; 28 import android.os.SystemClock; 29 import android.os.UserHandle; 30 import android.telecom.TelecomManager; 31 import android.util.Log; 32 33 import androidx.annotation.Nullable; 34 import androidx.annotation.VisibleForTesting; 35 36 import com.android.internal.logging.MetricsLogger; 37 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 38 import com.android.internal.widget.LockPatternUtils; 39 import com.android.keyguard.dagger.KeyguardBouncerScope; 40 import com.android.systemui.dagger.qualifiers.Background; 41 import com.android.systemui.dagger.qualifiers.Main; 42 import com.android.systemui.shade.ShadeController; 43 import com.android.systemui.statusbar.policy.ConfigurationController; 44 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; 45 import com.android.systemui.user.domain.interactor.SelectedUserInteractor; 46 import com.android.systemui.util.EmergencyDialerConstants; 47 import com.android.systemui.util.ViewController; 48 49 import java.util.concurrent.Executor; 50 51 import javax.inject.Inject; 52 53 /** View Controller for {@link com.android.keyguard.EmergencyButton}. */ 54 @KeyguardBouncerScope 55 public class EmergencyButtonController extends ViewController<EmergencyButton> { 56 private static final String TAG = "EmergencyButton"; 57 private final ConfigurationController mConfigurationController; 58 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 59 private final PowerManager mPowerManager; 60 private final ActivityTaskManager mActivityTaskManager; 61 private final ShadeController mShadeController; 62 private final TelecomManager mTelecomManager; 63 private final MetricsLogger mMetricsLogger; 64 65 private EmergencyButtonCallback mEmergencyButtonCallback; 66 private final LockPatternUtils mLockPatternUtils; 67 private final Executor mMainExecutor; 68 private final Executor mBackgroundExecutor; 69 private final SelectedUserInteractor mSelectedUserInteractor; 70 71 private final KeyguardUpdateMonitorCallback mInfoCallback = 72 new KeyguardUpdateMonitorCallback() { 73 @Override 74 public void onSimStateChanged(int subId, int slotId, int simState) { 75 updateEmergencyCallButton(); 76 } 77 78 @Override 79 public void onPhoneStateChanged(int phoneState) { 80 updateEmergencyCallButton(); 81 } 82 }; 83 84 private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { 85 @Override 86 public void onConfigChanged(Configuration newConfig) { 87 updateEmergencyCallButton(); 88 } 89 }; 90 91 @VisibleForTesting EmergencyButtonController(@ullable EmergencyButton view, ConfigurationController configurationController, KeyguardUpdateMonitor keyguardUpdateMonitor, PowerManager powerManager, ActivityTaskManager activityTaskManager, ShadeController shadeController, @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, Executor mainExecutor, Executor backgroundExecutor, SelectedUserInteractor selectedUserInteractor)92 public EmergencyButtonController(@Nullable EmergencyButton view, 93 ConfigurationController configurationController, 94 KeyguardUpdateMonitor keyguardUpdateMonitor, 95 PowerManager powerManager, 96 ActivityTaskManager activityTaskManager, 97 ShadeController shadeController, 98 @Nullable TelecomManager telecomManager, 99 MetricsLogger metricsLogger, 100 LockPatternUtils lockPatternUtils, 101 Executor mainExecutor, Executor backgroundExecutor, 102 SelectedUserInteractor selectedUserInteractor) { 103 super(view); 104 mConfigurationController = configurationController; 105 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 106 mPowerManager = powerManager; 107 mActivityTaskManager = activityTaskManager; 108 mShadeController = shadeController; 109 mTelecomManager = telecomManager; 110 mMetricsLogger = metricsLogger; 111 mLockPatternUtils = lockPatternUtils; 112 mMainExecutor = mainExecutor; 113 mBackgroundExecutor = backgroundExecutor; 114 mSelectedUserInteractor = selectedUserInteractor; 115 } 116 117 @Override onInit()118 protected void onInit() { 119 whitelistIpcs(this::updateEmergencyCallButton); 120 } 121 122 @Override onViewAttached()123 protected void onViewAttached() { 124 mKeyguardUpdateMonitor.registerCallback(mInfoCallback); 125 mConfigurationController.addCallback(mConfigurationListener); 126 mView.setOnClickListener(v -> takeEmergencyCallAction()); 127 } 128 129 @Override onViewDetached()130 protected void onViewDetached() { 131 mKeyguardUpdateMonitor.removeCallback(mInfoCallback); 132 mConfigurationController.removeCallback(mConfigurationListener); 133 } 134 135 /** 136 * Updates the visibility of the emergency button. 137 * 138 * This method runs binder calls in a background thread. 139 */ 140 @VisibleForTesting 141 @SuppressLint("MissingPermission") updateEmergencyCallButton()142 public void updateEmergencyCallButton() { 143 if (mView != null) { 144 // Run in bg thread to avoid throttling the main thread with binder call. 145 mBackgroundExecutor.execute(() -> { 146 boolean isInCall = mTelecomManager != null && mTelecomManager.isInCall(); 147 boolean isSecure = mLockPatternUtils 148 .isSecure(mSelectedUserInteractor.getSelectedUserId()); 149 mMainExecutor.execute(() -> mView.updateEmergencyCallButton( 150 /* isInCall= */ isInCall, 151 /* hasTelephonyRadio= */ getContext().getPackageManager() 152 .hasSystemFeature(PackageManager.FEATURE_TELEPHONY), 153 /* simLocked= */ mKeyguardUpdateMonitor.isSimPinVoiceSecure(), 154 /* isSecure= */ isSecure)); 155 }); 156 } 157 } 158 setEmergencyButtonCallback(EmergencyButtonCallback callback)159 public void setEmergencyButtonCallback(EmergencyButtonCallback callback) { 160 mEmergencyButtonCallback = callback; 161 } 162 /** 163 * Shows the emergency dialer or returns the user to the existing call. 164 */ 165 @SuppressLint("MissingPermission") takeEmergencyCallAction()166 public void takeEmergencyCallAction() { 167 mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL); 168 if (mPowerManager != null) { 169 mPowerManager.userActivity(SystemClock.uptimeMillis(), true); 170 } 171 mActivityTaskManager.stopSystemLockTaskMode(); 172 mShadeController.collapseShade(false); 173 // Run in bg thread to avoid throttling the main thread with binder call. 174 mBackgroundExecutor.execute(() -> { 175 boolean isInCall = mTelecomManager != null && mTelecomManager.isInCall(); 176 mMainExecutor.execute(() -> { 177 if (isInCall) { 178 mTelecomManager.showInCallScreen(false); 179 if (mEmergencyButtonCallback != null) { 180 mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall(); 181 } 182 } else { 183 mKeyguardUpdateMonitor.reportEmergencyCallAction(true /* bypassHandler */); 184 if (mTelecomManager == null) { 185 Log.wtf(TAG, "TelecomManager was null, cannot launch emergency dialer"); 186 return; 187 } 188 Intent emergencyDialIntent = 189 mTelecomManager.createLaunchEmergencyDialerIntent(null /* number*/) 190 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 191 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 192 | Intent.FLAG_ACTIVITY_CLEAR_TOP) 193 .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, 194 EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON); 195 196 getContext().startActivityAsUser(emergencyDialIntent, 197 ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(), 198 new UserHandle(mSelectedUserInteractor.getSelectedUserId())); 199 } 200 }); 201 }); 202 } 203 204 /** */ 205 public interface EmergencyButtonCallback { 206 /** */ onEmergencyButtonClickedWhenInCall()207 void onEmergencyButtonClickedWhenInCall(); 208 } 209 210 /** Injectable Factory for creating {@link EmergencyButtonController}. */ 211 public static class Factory { 212 private final ConfigurationController mConfigurationController; 213 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 214 private final PowerManager mPowerManager; 215 private final ActivityTaskManager mActivityTaskManager; 216 private final ShadeController mShadeController; 217 @Nullable 218 private final TelecomManager mTelecomManager; 219 private final MetricsLogger mMetricsLogger; 220 private final LockPatternUtils mLockPatternUtils; 221 private final Executor mMainExecutor; 222 private final Executor mBackgroundExecutor; 223 private final SelectedUserInteractor mSelectedUserInteractor; 224 225 @Inject Factory(ConfigurationController configurationController, KeyguardUpdateMonitor keyguardUpdateMonitor, PowerManager powerManager, ActivityTaskManager activityTaskManager, ShadeController shadeController, @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, @Main Executor mainExecutor, @Background Executor backgroundExecutor, SelectedUserInteractor selectedUserInteractor)226 public Factory(ConfigurationController configurationController, 227 KeyguardUpdateMonitor keyguardUpdateMonitor, 228 PowerManager powerManager, 229 ActivityTaskManager activityTaskManager, 230 ShadeController shadeController, 231 @Nullable TelecomManager telecomManager, 232 MetricsLogger metricsLogger, 233 LockPatternUtils lockPatternUtils, 234 @Main Executor mainExecutor, 235 @Background Executor backgroundExecutor, 236 SelectedUserInteractor selectedUserInteractor) { 237 238 mConfigurationController = configurationController; 239 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 240 mPowerManager = powerManager; 241 mActivityTaskManager = activityTaskManager; 242 mShadeController = shadeController; 243 mTelecomManager = telecomManager; 244 mMetricsLogger = metricsLogger; 245 mLockPatternUtils = lockPatternUtils; 246 mMainExecutor = mainExecutor; 247 mBackgroundExecutor = backgroundExecutor; 248 mSelectedUserInteractor = selectedUserInteractor; 249 } 250 251 /** Construct an {@link com.android.keyguard.EmergencyButtonController}. */ create(EmergencyButton view)252 public EmergencyButtonController create(EmergencyButton view) { 253 return new EmergencyButtonController(view, mConfigurationController, 254 mKeyguardUpdateMonitor, mPowerManager, mActivityTaskManager, mShadeController, 255 mTelecomManager, mMetricsLogger, mLockPatternUtils, mMainExecutor, 256 mBackgroundExecutor, mSelectedUserInteractor); 257 } 258 } 259 } 260