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