blob: 9b5373c9c5f57625cb617e106866f20bf9af4858 [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;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.net.Uri;
26import com.android.internal.telephony.TelephonyIntents;
27import com.android.internal.telephony.Phone;
28import android.telephony.PhoneNumberUtils;
29import android.util.Log;
30import android.view.WindowManager;
31
32import 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 */
54public 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. */
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 */
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 */
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 */
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 */
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
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
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
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
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
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
260 private static void log(String msg) {
261 Log.d(TAG, "[SpecialCharSequenceMgr] " + msg);
262 }
263}