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