• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.annotation.NonNull;
20 import android.app.AlertDialog;
21 import android.app.AlertDialog.Builder;
22 import android.app.Dialog;
23 import android.app.ProgressDialog;
24 import android.content.res.ColorStateList;
25 import android.content.res.Resources;
26 import android.content.res.TypedArray;
27 import android.graphics.Color;
28 import android.telephony.PinResult;
29 import android.telephony.SubscriptionInfo;
30 import android.telephony.SubscriptionManager;
31 import android.telephony.TelephonyManager;
32 import android.util.Log;
33 import android.view.View;
34 import android.view.WindowManager;
35 import android.widget.ImageView;
36 
37 import com.android.internal.util.LatencyTracker;
38 import com.android.internal.widget.LockPatternUtils;
39 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
40 import com.android.systemui.R;
41 import com.android.systemui.classifier.FalsingCollector;
42 
43 public class KeyguardSimPinViewController
44         extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
45     public static final String TAG = "KeyguardSimPinView";
46     private static final String LOG_TAG = "KeyguardSimPinView";
47     private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
48     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
49     private final TelephonyManager mTelephonyManager;
50 
51     private ProgressDialog mSimUnlockProgressDialog;
52     private CheckSimPin mCheckSimPinThread;
53     private int mRemainingAttempts;
54     // Below flag is set to true during power-up or when a new SIM card inserted on device.
55     // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
56     // be displayed to inform user about the number of remaining PIN attempts left.
57     private boolean mShowDefaultMessage;
58     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
59     private AlertDialog mRemainingAttemptsDialog;
60     private ImageView mSimImageView;
61 
62     KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
63         @Override
64         public void onSimStateChanged(int subId, int slotId, int simState) {
65             if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
66             if (simState == TelephonyManager.SIM_STATE_READY) {
67                 mRemainingAttempts = -1;
68                 resetState();
69             } else {
70                 resetState();
71             }
72         }
73     };
74 
KeyguardSimPinViewController(KeyguardSimPinView view, KeyguardUpdateMonitor keyguardUpdateMonitor, SecurityMode securityMode, LockPatternUtils lockPatternUtils, KeyguardSecurityCallback keyguardSecurityCallback, KeyguardMessageAreaController.Factory messageAreaControllerFactory, LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener, TelephonyManager telephonyManager, FalsingCollector falsingCollector, EmergencyButtonController emergencyButtonController)75     protected KeyguardSimPinViewController(KeyguardSimPinView view,
76             KeyguardUpdateMonitor keyguardUpdateMonitor,
77             SecurityMode securityMode, LockPatternUtils lockPatternUtils,
78             KeyguardSecurityCallback keyguardSecurityCallback,
79             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
80             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
81             TelephonyManager telephonyManager, FalsingCollector falsingCollector,
82             EmergencyButtonController emergencyButtonController) {
83         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
84                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
85                 emergencyButtonController, falsingCollector);
86         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
87         mTelephonyManager = telephonyManager;
88         mSimImageView = mView.findViewById(R.id.keyguard_sim);
89     }
90 
91     @Override
onViewAttached()92     protected void onViewAttached() {
93         super.onViewAttached();
94     }
95 
96     @Override
resetState()97     void resetState() {
98         super.resetState();
99         if (DEBUG) Log.v(TAG, "Resetting state");
100         handleSubInfoChangeIfNeeded();
101         mMessageAreaController.setMessage("");
102         if (mShowDefaultMessage) {
103             showDefaultMessage();
104         }
105 
106         mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
107     }
108 
109     @Override
startDisappearAnimation(Runnable finishRunnable)110     public boolean startDisappearAnimation(Runnable finishRunnable) {
111         return false;
112     }
113 
114     @Override
onResume(int reason)115     public void onResume(int reason) {
116         super.onResume(reason);
117         mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
118         mView.resetState();
119     }
120 
121     @Override
onPause()122     public void onPause() {
123         super.onPause();
124         mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
125 
126         // dismiss the dialog.
127         if (mSimUnlockProgressDialog != null) {
128             mSimUnlockProgressDialog.dismiss();
129             mSimUnlockProgressDialog = null;
130         }
131     }
132 
133     @Override
verifyPasswordAndUnlock()134     protected void verifyPasswordAndUnlock() {
135         String entry = mPasswordEntry.getText();
136 
137         if (entry.length() < 4) {
138             // otherwise, display a message to the user, and don't submit.
139             mMessageAreaController.setMessage(
140                     com.android.systemui.R.string.kg_invalid_sim_pin_hint);
141             mView.resetPasswordText(true /* animate */, true /* announce */);
142             getKeyguardSecurityCallback().userActivity();
143             return;
144         }
145 
146         getSimUnlockProgressDialog().show();
147 
148         if (mCheckSimPinThread == null) {
149             mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
150                 @Override
151                 void onSimCheckResponse(final PinResult result) {
152                     mView.post(() -> {
153                         mRemainingAttempts = result.getAttemptsRemaining();
154                         if (mSimUnlockProgressDialog != null) {
155                             mSimUnlockProgressDialog.hide();
156                         }
157                         mView.resetPasswordText(true /* animate */,
158                                 /* announce */
159                                 result.getResult() != PinResult.PIN_RESULT_TYPE_SUCCESS);
160                         if (result.getResult() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
161                             mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
162                             mRemainingAttempts = -1;
163                             mShowDefaultMessage = true;
164                             getKeyguardSecurityCallback().dismiss(
165                                     true, KeyguardUpdateMonitor.getCurrentUser());
166                         } else {
167                             mShowDefaultMessage = false;
168                             if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
169                                 if (result.getAttemptsRemaining() <= 2) {
170                                     // this is getting critical - show dialog
171                                     getSimRemainingAttemptsDialog(
172                                             result.getAttemptsRemaining()).show();
173                                 } else {
174                                     // show message
175                                     mMessageAreaController.setMessage(
176                                             getPinPasswordErrorMessage(
177                                                     result.getAttemptsRemaining(), false));
178                                 }
179                             } else {
180                                 // "PIN operation failed!" - no idea what this was and no way to
181                                 // find out. :/
182                                 mMessageAreaController.setMessage(mView.getResources().getString(
183                                         R.string.kg_password_pin_failed));
184                             }
185                             if (DEBUG) {
186                                 Log.d(LOG_TAG, "verifyPasswordAndUnlock "
187                                         + " CheckSimPin.onSimCheckResponse: " + result
188                                         + " attemptsRemaining=" + result.getAttemptsRemaining());
189                             }
190                         }
191                         getKeyguardSecurityCallback().userActivity();
192                         mCheckSimPinThread = null;
193                     });
194                 }
195             };
196             mCheckSimPinThread.start();
197         }
198     }
199 
getSimUnlockProgressDialog()200     private Dialog getSimUnlockProgressDialog() {
201         if (mSimUnlockProgressDialog == null) {
202             mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
203             mSimUnlockProgressDialog.setMessage(
204                     mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
205             mSimUnlockProgressDialog.setIndeterminate(true);
206             mSimUnlockProgressDialog.setCancelable(false);
207             mSimUnlockProgressDialog.getWindow().setType(
208                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
209         }
210         return mSimUnlockProgressDialog;
211     }
212 
213 
getSimRemainingAttemptsDialog(int remaining)214     private Dialog getSimRemainingAttemptsDialog(int remaining) {
215         String msg = getPinPasswordErrorMessage(remaining, false);
216         if (mRemainingAttemptsDialog == null) {
217             Builder builder = new AlertDialog.Builder(mView.getContext());
218             builder.setMessage(msg);
219             builder.setCancelable(false);
220             builder.setNeutralButton(R.string.ok, null);
221             mRemainingAttemptsDialog = builder.create();
222             mRemainingAttemptsDialog.getWindow().setType(
223                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
224         } else {
225             mRemainingAttemptsDialog.setMessage(msg);
226         }
227         return mRemainingAttemptsDialog;
228     }
229 
230 
getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault)231     private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
232         String displayMessage;
233         int msgId;
234         if (attemptsRemaining == 0) {
235             displayMessage = mView.getResources().getString(
236                     R.string.kg_password_wrong_pin_code_pukked);
237         } else if (attemptsRemaining > 0) {
238             msgId = isDefault ? R.plurals.kg_password_default_pin_message :
239                     R.plurals.kg_password_wrong_pin_code;
240             displayMessage = mView.getResources()
241                     .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
242         } else {
243             msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
244             displayMessage = mView.getResources().getString(msgId);
245         }
246         if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) {
247             displayMessage = mView.getResources()
248                     .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
249         }
250         if (DEBUG) {
251             Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
252                     + attemptsRemaining + " displayMessage=" + displayMessage);
253         }
254         return displayMessage;
255     }
256 
showDefaultMessage()257     private void showDefaultMessage() {
258         setLockedSimMessage();
259         if (mRemainingAttempts >= 0) {
260             return;
261         }
262 
263         // Sending empty PIN here to query the number of remaining PIN attempts
264         new CheckSimPin("", mSubId) {
265             void onSimCheckResponse(final PinResult result) {
266                 Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
267                         + result.toString());
268                 if (result.getAttemptsRemaining() >= 0) {
269                     mRemainingAttempts = result.getAttemptsRemaining();
270                     setLockedSimMessage();
271                 }
272             }
273         }.start();
274     }
275 
276     /**
277      * Since the IPC can block, we want to run the request in a separate thread
278      * with a callback.
279      */
280     private abstract class CheckSimPin extends Thread {
281         private final String mPin;
282         private int mSubId;
283 
CheckSimPin(String pin, int subId)284         protected CheckSimPin(String pin, int subId) {
285             mPin = pin;
286             mSubId = subId;
287         }
288 
onSimCheckResponse(@onNull PinResult result)289         abstract void onSimCheckResponse(@NonNull PinResult result);
290 
291         @Override
run()292         public void run() {
293             if (DEBUG) {
294                 Log.v(TAG, "call supplyIccLockPin(subid=" + mSubId + ")");
295             }
296             TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
297             final PinResult result = telephonyManager.supplyIccLockPin(mPin);
298             if (DEBUG) {
299                 Log.v(TAG, "supplyIccLockPin returned: " + result.toString());
300             }
301             mView.post(() -> onSimCheckResponse(result));
302         }
303     }
304 
setLockedSimMessage()305     private void setLockedSimMessage() {
306         boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
307         int count = 1;
308         if (mTelephonyManager != null) {
309             count = mTelephonyManager.getActiveModemCount();
310         }
311         Resources rez = mView.getResources();
312         String msg;
313         TypedArray array = mView.getContext().obtainStyledAttributes(
314                 new int[] { android.R.attr.textColor });
315         int color = array.getColor(0, Color.WHITE);
316         array.recycle();
317         if (count < 2) {
318             msg = rez.getString(R.string.kg_sim_pin_instructions);
319         } else {
320             SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId);
321             CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
322             msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
323             if (info != null) {
324                 color = info.getIconTint();
325             }
326         }
327         if (isEsimLocked) {
328             msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
329         }
330 
331         if (mView.getVisibility() == View.VISIBLE) {
332             mMessageAreaController.setMessage(msg);
333         }
334         mSimImageView.setImageTintList(ColorStateList.valueOf(color));
335     }
336 
handleSubInfoChangeIfNeeded()337     private void handleSubInfoChangeIfNeeded() {
338         int subId = mKeyguardUpdateMonitor
339                 .getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
340         if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
341             mSubId = subId;
342             mShowDefaultMessage = true;
343             mRemainingAttempts = -1;
344         }
345     }
346 }
347