blob: d0fe2c18aae03327e1b92f9a4cd02088479475e9 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 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.phone;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.ActivityNotFoundException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070022import android.content.Context;
23import android.content.Intent;
Jack Yu58d04bd2024-09-10 17:32:39 -070024import android.os.UserHandle;
lei.huang04c48672019-03-13 16:25:39 +080025import android.os.UserManager;
Jeff Sharkey236e67c2014-04-16 17:21:29 -070026import android.provider.Settings;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070027import android.telephony.PhoneNumberUtils;
Youming Yec3fb55d2019-03-20 11:14:07 -070028import android.telephony.SubscriptionInfo;
29import android.telephony.SubscriptionManager;
30import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070031import android.util.Log;
32import android.view.WindowManager;
33
Youming Yec3fb55d2019-03-20 11:14:07 -070034import com.android.internal.telephony.IccCardConstants;
Aravind Sreekumarafc08c52018-04-10 15:34:32 -070035import com.android.internal.telephony.Phone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070036import com.android.internal.telephony.TelephonyCapabilities;
Ling Ma7fb1fcf2023-12-05 14:40:16 -080037import com.android.internal.telephony.flags.Flags;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038
Youming Yec3fb55d2019-03-20 11:14:07 -070039import java.util.ArrayList;
40import java.util.List;
41
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042/**
43 * Helper class to listen for some magic dialpad character sequences
44 * that are handled specially by the Phone app.
45 *
46 * Note the Contacts app also handles these sequences too, so there's a
47 * separate version of this class under apps/Contacts.
48 *
49 * In fact, the most common use case for these special sequences is typing
50 * them from the regular "Dialer" used for outgoing calls, which is part
51 * of the contacts app; see DialtactsActivity and DialpadFragment.
52 * *This* version of SpecialCharSequenceMgr is used for only a few
53 * relatively obscure places in the UI:
54 * - The "SIM network unlock" PIN entry screen (see
55 * IccNetworkDepersonalizationPanel.java)
56 * - The emergency dialer (see EmergencyDialer.java).
57 *
58 * TODO: there's lots of duplicated code between this class and the
59 * corresponding class under apps/Contacts. Let's figure out a way to
60 * unify these two classes (in the framework? in a common shared library?)
61 */
62public class SpecialCharSequenceMgr {
63 private static final String TAG = PhoneGlobals.LOG_TAG;
64 private static final boolean DBG = false;
65
66 private static final String MMI_IMEI_DISPLAY = "*#06#";
67 private static final String MMI_REGULATORY_INFO_DISPLAY = "*#07#";
68
69 /** This class is never instantiated. */
70 private SpecialCharSequenceMgr() {
71 }
72
73 /**
74 * Check for special strings of digits from an input
75 * string.
76 * @param context input Context for the events we handle.
77 * @param input the dial string to be examined.
78 */
79 static boolean handleChars(Context context, String input) {
80 return handleChars(context, input, null);
81 }
82
83 /**
84 * Generally used for the Personal Unblocking Key (PUK) unlocking
85 * case, where we want to be able to maintain a handle to the
86 * calling activity so that we can close it or otherwise display
87 * indication if the PUK code is recognized.
88 *
89 * NOTE: The counterpart to this file in Contacts does
90 * NOT contain the special PUK handling code, since it
91 * does NOT need it. When the device gets into PUK-
92 * locked state, the keyguard comes up and the only way
93 * to unlock the device is through the Emergency dialer,
94 * which is still in the Phone App.
95 *
96 * @param context input Context for the events we handle.
97 * @param input the dial string to be examined.
98 * @param pukInputActivity activity that originated this
99 * PUK call, tracked so that we can close it or otherwise
100 * indicate that special character sequence is
101 * successfully processed. Can be null.
102 * @return true if the input was a special string which has been
103 * handled.
104 */
105 static boolean handleChars(Context context,
106 String input,
107 Activity pukInputActivity) {
108
109 //get rid of the separators so that the string gets parsed correctly
110 String dialString = PhoneNumberUtils.stripSeparators(input);
111
112 if (handleIMEIDisplay(context, dialString)
113 || handleRegulatoryInfoDisplay(context, dialString)
114 || handlePinEntry(context, dialString, pukInputActivity)
115 || handleAdnEntry(context, dialString)
fionaxue559f972017-01-24 22:31:12 -0800116 || handleSecretCode(dialString)) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700117 return true;
118 }
119
120 return false;
121 }
122
123 /**
124 * Variant of handleChars() that looks for the subset of "special
125 * sequences" that are available even if the device is locked.
126 *
127 * (Specifically, these are the sequences that you're allowed to type
128 * in the Emergency Dialer, which is accessible *without* unlocking
129 * the device.)
130 */
131 static boolean handleCharsForLockedDevice(Context context,
132 String input,
133 Activity pukInputActivity) {
134 // Get rid of the separators so that the string gets parsed correctly
135 String dialString = PhoneNumberUtils.stripSeparators(input);
136
137 // The only sequences available on a locked device are the "**04"
138 // or "**05" sequences that allow you to enter PIN or PUK-related
139 // codes. (e.g. for the case where you're currently locked out of
140 // your phone, and need to change the PIN! The only way to do
141 // that is via the Emergency Dialer.)
142
143 if (handlePinEntry(context, dialString, pukInputActivity)) {
144 return true;
145 }
146
147 return false;
148 }
149
150 /**
fionaxue559f972017-01-24 22:31:12 -0800151 * Handles secret codes to launch arbitrary receivers in the form of *#*#<code>#*#*.
152 * If a secret code is encountered, an broadcast intent is sent with the
153 * android_secret_code://<code> URI.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700154 *
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700155 * @param input the text to check for a secret code in
fionaxue559f972017-01-24 22:31:12 -0800156 * @return true if a secret code was encountered and intent is sent out
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700157 */
fionaxue559f972017-01-24 22:31:12 -0800158 static private boolean handleSecretCode(String input) {
fionaxu235cc5e2017-03-06 22:25:57 -0800159 // Secret codes are in the form *#*#<code>#*#*
160 int len = input.length();
161 if (len > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
162 final Phone phone = PhoneGlobals.getPhone();
163 phone.sendDialerSpecialCode(input.substring(4, len - 4));
164 return true;
165 }
166 return false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700167 }
168
169 static private boolean handleAdnEntry(Context context, String input) {
170 /* ADN entries are of the form "N(N)(N)#" */
171
172 // if the phone is keyguard-restricted, then just ignore this
173 // input. We want to make sure that sim card contacts are NOT
174 // exposed unless the phone is unlocked, and this code can be
175 // accessed from the emergency dialer.
176 if (PhoneGlobals.getInstance().getKeyguardManager().inKeyguardRestrictedInputMode()) {
177 return false;
178 }
179
180 int len = input.length();
181 if ((len > 1) && (len < 5) && (input.endsWith("#"))) {
182 try {
183 int index = Integer.parseInt(input.substring(0, len-1));
184 Intent intent = new Intent(Intent.ACTION_PICK);
185
186 intent.setClassName("com.android.phone",
187 "com.android.phone.SimContacts");
188 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
189 intent.putExtra("index", index);
Jack Yu58d04bd2024-09-10 17:32:39 -0700190 PhoneGlobals.getInstance().startActivityAsUser(intent, UserHandle.CURRENT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700191
192 return true;
193 } catch (NumberFormatException ex) {}
194 }
195 return false;
196 }
197
Youming Yec3fb55d2019-03-20 11:14:07 -0700198 private static IccCardConstants.State getSimState(int slotId, Context context) {
199 final TelephonyManager tele = TelephonyManager.from(context);
200 int simState = tele.getSimState(slotId);
201 IccCardConstants.State state;
202 try {
203 state = IccCardConstants.State.intToState(simState);
204 } catch (IllegalArgumentException ex) {
205 Log.w(TAG, "Unknown sim state: " + simState);
206 state = IccCardConstants.State.UNKNOWN;
207 }
208 return state;
209 }
210
211 private static int getNextSubIdForState(IccCardConstants.State state, Context context) {
212 SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
Ling Ma7fb1fcf2023-12-05 14:40:16 -0800213 if (Flags.workProfileApiSplit()) {
214 subscriptionManager = subscriptionManager.createForAllUserProfiles();
215 }
Youming Yec3fb55d2019-03-20 11:14:07 -0700216 List<SubscriptionInfo> list = subscriptionManager.getActiveSubscriptionInfoList();
217 if (list == null) {
218 // getActiveSubscriptionInfoList was null callers expect an empty list.
219 list = new ArrayList<>();
220 }
221 int resultId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
222 int bestSlotId = Integer.MAX_VALUE; // Favor lowest slot first
223 for (int i = 0; i < list.size(); i++) {
224 final SubscriptionInfo info = list.get(i);
225 final int id = info.getSubscriptionId();
226 if (state == getSimState(info.getSimSlotIndex(), context)
227 && bestSlotId > info.getSimSlotIndex()) {
228 resultId = id;
229 bestSlotId = info.getSimSlotIndex();
230 }
231 }
232 return resultId;
233 }
234
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700235 static private boolean handlePinEntry(Context context, String input,
236 Activity pukInputActivity) {
237 // TODO: The string constants here should be removed in favor
238 // of some call to a static the MmiCode class that determines
239 // if a dialstring is an MMI code.
240 if ((input.startsWith("**04") || input.startsWith("**05"))
241 && input.endsWith("#")) {
lei.huang04c48672019-03-13 16:25:39 +0800242 UserManager userManager = (UserManager) pukInputActivity
243 .getSystemService(Context.USER_SERVICE);
244 if (userManager.isSystemUser()) {
245 PhoneGlobals app = PhoneGlobals.getInstance();
246 Phone phone;
247 int subId;
248 if (input.startsWith("**04")) {
249 subId = getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED, context);
250 } else {
251 subId = getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED, context);
252 }
253 if (SubscriptionManager.isValidSubscriptionId(subId)) {
254 log("get phone with subId: " + subId);
255 phone = PhoneGlobals.getPhone(subId);
256 } else {
257 log("get default phone");
258 phone = PhoneGlobals.getPhone();
259 }
260 boolean isMMIHandled = phone.handlePinMmi(input);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700261
lei.huang04c48672019-03-13 16:25:39 +0800262 // if the PUK code is recognized then indicate to the
263 // phone app that an attempt to unPUK the device was
264 // made with this activity. The PUK code may still
265 // fail though, but we won't know until the MMI code
266 // returns a result.
267 if (isMMIHandled && input.startsWith("**05")) {
268 app.setPukEntryActivity(pukInputActivity);
269 }
270 return isMMIHandled;
271 } else {
Sooraj Sasindran9dbb2882021-10-19 11:40:34 -0700272 AlertDialog dialog = FrameworksUtils.makeAlertDialogBuilder(context)
lei.huang04c48672019-03-13 16:25:39 +0800273 .setMessage(R.string.pin_puk_system_user_only)
274 .setPositiveButton(R.string.ok, null)
275 .setCancelable(true).create();
276 dialog.show();
277 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
278 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
279 return true;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700280 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700281 }
282 return false;
283 }
284
285 static private boolean handleIMEIDisplay(Context context,
286 String input) {
287 if (input.equals(MMI_IMEI_DISPLAY)) {
288 showDeviceIdPanel(context);
289 return true;
290 }
291
292 return false;
293 }
294
295 static private void showDeviceIdPanel(Context context) {
296 if (DBG) log("showDeviceIdPanel()...");
297
298 Phone phone = PhoneGlobals.getPhone();
299 int labelId = TelephonyCapabilities.getDeviceIdLabel(phone);
300 String deviceId = phone.getDeviceId();
301
Sooraj Sasindran9dbb2882021-10-19 11:40:34 -0700302 AlertDialog alert = FrameworksUtils.makeAlertDialogBuilder(context)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700303 .setTitle(labelId)
304 .setMessage(deviceId)
305 .setPositiveButton(R.string.ok, null)
306 .setCancelable(false)
307 .create();
308 alert.getWindow().setType(WindowManager.LayoutParams.TYPE_PRIORITY_PHONE);
309 alert.show();
310 }
311
312 private static boolean handleRegulatoryInfoDisplay(Context context, String input) {
313 if (input.equals(MMI_REGULATORY_INFO_DISPLAY)) {
314 log("handleRegulatoryInfoDisplay() sending intent to settings app");
Jeff Sharkey236e67c2014-04-16 17:21:29 -0700315 Intent showRegInfoIntent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700316 try {
Jack Yu58d04bd2024-09-10 17:32:39 -0700317 context.startActivityAsUser(showRegInfoIntent, UserHandle.CURRENT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700318 } catch (ActivityNotFoundException e) {
Jack Yu58d04bd2024-09-10 17:32:39 -0700319 Log.e(TAG, "startActivityAsUser() failed: " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700320 }
321 return true;
322 }
323 return false;
324 }
325
326 private static void log(String msg) {
327 Log.d(TAG, "[SpecialCharSequenceMgr] " + msg);
328 }
329}