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