1/* 2 * Copyright (C) 2008 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 17package com.android.keyguard; 18 19import android.app.ActivityManagerNative; 20import android.app.ActivityOptions; 21import android.content.Context; 22import android.content.Intent; 23import android.content.res.Configuration; 24import android.os.PowerManager; 25import android.os.RemoteException; 26import android.os.SystemClock; 27import android.os.UserHandle; 28import android.telecom.TelecomManager; 29import android.util.AttributeSet; 30import android.util.Slog; 31import android.view.View; 32import android.widget.Button; 33 34import com.android.internal.logging.MetricsLogger; 35import com.android.internal.logging.MetricsProto.MetricsEvent; 36import com.android.internal.telephony.IccCardConstants.State; 37import com.android.internal.widget.LockPatternUtils; 38 39/** 40 * This class implements a smart emergency button that updates itself based 41 * on telephony state. When the phone is idle, it is an emergency call button. 42 * When there's a call in progress, it presents an appropriate message and 43 * allows the user to return to the call. 44 */ 45public class EmergencyButton extends Button { 46 private static final Intent INTENT_EMERGENCY_DIAL = new Intent() 47 .setAction("com.android.phone.EmergencyDialer.DIAL") 48 .setPackage("com.android.phone") 49 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 50 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 51 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 52 53 private static final String LOG_TAG = "EmergencyButton"; 54 55 KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { 56 57 @Override 58 public void onSimStateChanged(int subId, int slotId, State simState) { 59 updateEmergencyCallButton(); 60 } 61 62 @Override 63 public void onPhoneStateChanged(int phoneState) { 64 updateEmergencyCallButton(); 65 } 66 }; 67 68 public interface EmergencyButtonCallback { 69 public void onEmergencyButtonClickedWhenInCall(); 70 } 71 72 private LockPatternUtils mLockPatternUtils; 73 private PowerManager mPowerManager; 74 private EmergencyButtonCallback mEmergencyButtonCallback; 75 76 private final boolean mIsVoiceCapable; 77 private final boolean mEnableEmergencyCallWhileSimLocked; 78 79 public EmergencyButton(Context context) { 80 this(context, null); 81 } 82 83 public EmergencyButton(Context context, AttributeSet attrs) { 84 super(context, attrs); 85 mIsVoiceCapable = context.getResources().getBoolean( 86 com.android.internal.R.bool.config_voice_capable); 87 mEnableEmergencyCallWhileSimLocked = mContext.getResources().getBoolean( 88 com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked); 89 } 90 91 @Override 92 protected void onAttachedToWindow() { 93 super.onAttachedToWindow(); 94 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback); 95 } 96 97 @Override 98 protected void onDetachedFromWindow() { 99 super.onDetachedFromWindow(); 100 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback); 101 } 102 103 @Override 104 protected void onFinishInflate() { 105 super.onFinishInflate(); 106 mLockPatternUtils = new LockPatternUtils(mContext); 107 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 108 setOnClickListener(new OnClickListener() { 109 public void onClick(View v) { 110 takeEmergencyCallAction(); 111 } 112 }); 113 updateEmergencyCallButton(); 114 } 115 116 @Override 117 protected void onConfigurationChanged(Configuration newConfig) { 118 super.onConfigurationChanged(newConfig); 119 updateEmergencyCallButton(); 120 } 121 122 /** 123 * Shows the emergency dialer or returns the user to the existing call. 124 */ 125 public void takeEmergencyCallAction() { 126 MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_CALL); 127 // TODO: implement a shorter timeout once new PowerManager API is ready. 128 // should be the equivalent to the old userActivity(EMERGENCY_CALL_TIMEOUT) 129 mPowerManager.userActivity(SystemClock.uptimeMillis(), true); 130 try { 131 ActivityManagerNative.getDefault().stopSystemLockTaskMode(); 132 } catch (RemoteException e) { 133 Slog.w(LOG_TAG, "Failed to stop app pinning"); 134 } 135 if (isInCall()) { 136 resumeCall(); 137 if (mEmergencyButtonCallback != null) { 138 mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall(); 139 } 140 } else { 141 KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction( 142 true /* bypassHandler */); 143 getContext().startActivityAsUser(INTENT_EMERGENCY_DIAL, 144 ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(), 145 new UserHandle(KeyguardUpdateMonitor.getCurrentUser())); 146 } 147 } 148 149 private void updateEmergencyCallButton() { 150 boolean visible = false; 151 if (mIsVoiceCapable) { 152 // Emergency calling requires voice capability. 153 if (isInCall()) { 154 visible = true; // always show "return to call" if phone is off-hook 155 } else { 156 final boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext) 157 .isSimPinVoiceSecure(); 158 if (simLocked) { 159 // Some countries can't handle emergency calls while SIM is locked. 160 visible = mEnableEmergencyCallWhileSimLocked; 161 } else { 162 // Only show if there is a secure screen (pin/pattern/SIM pin/SIM puk); 163 visible = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()); 164 } 165 } 166 } 167 if (visible) { 168 setVisibility(View.VISIBLE); 169 170 int textId; 171 if (isInCall()) { 172 textId = com.android.internal.R.string.lockscreen_return_to_call; 173 } else { 174 textId = com.android.internal.R.string.lockscreen_emergency_call; 175 } 176 setText(textId); 177 } else { 178 setVisibility(View.GONE); 179 } 180 } 181 182 public void setCallback(EmergencyButtonCallback callback) { 183 mEmergencyButtonCallback = callback; 184 } 185 186 /** 187 * Resumes a call in progress. 188 */ 189 private void resumeCall() { 190 getTelecommManager().showInCallScreen(false); 191 } 192 193 /** 194 * @return {@code true} if there is a call currently in progress. 195 */ 196 private boolean isInCall() { 197 return getTelecommManager().isInCall(); 198 } 199 200 private TelecomManager getTelecommManager() { 201 return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); 202 } 203} 204