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 
17 package com.android.keyguard;
18 
19 import android.app.ActivityManagerNative;
20 import android.app.ActivityOptions;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.res.Configuration;
24 import android.os.PowerManager;
25 import android.os.RemoteException;
26 import android.os.SystemClock;
27 import android.os.UserHandle;
28 import android.telecom.TelecomManager;
29 import android.util.AttributeSet;
30 import android.util.Slog;
31 import android.view.View;
32 import android.widget.Button;
33 
34 import com.android.internal.logging.MetricsLogger;
35 import com.android.internal.logging.MetricsProto.MetricsEvent;
36 import com.android.internal.telephony.IccCardConstants.State;
37 import 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  */
45 public 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 {
onEmergencyButtonClickedWhenInCall()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 
EmergencyButton(Context context)79     public EmergencyButton(Context context) {
80         this(context, null);
81     }
82 
EmergencyButton(Context context, AttributeSet attrs)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
onAttachedToWindow()92     protected void onAttachedToWindow() {
93         super.onAttachedToWindow();
94         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
95     }
96 
97     @Override
onDetachedFromWindow()98     protected void onDetachedFromWindow() {
99         super.onDetachedFromWindow();
100         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
101     }
102 
103     @Override
onFinishInflate()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
onConfigurationChanged(Configuration newConfig)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      */
takeEmergencyCallAction()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 
updateEmergencyCallButton()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 
setCallback(EmergencyButtonCallback callback)182     public void setCallback(EmergencyButtonCallback callback) {
183         mEmergencyButtonCallback = callback;
184     }
185 
186     /**
187      * Resumes a call in progress.
188      */
resumeCall()189     private void resumeCall() {
190         getTelecommManager().showInCallScreen(false);
191     }
192 
193     /**
194      * @return {@code true} if there is a call currently in progress.
195      */
isInCall()196     private boolean isInCall() {
197         return getTelecommManager().isInCall();
198     }
199 
getTelecommManager()200     private TelecomManager getTelecommManager() {
201         return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
202     }
203 }
204