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. */ 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 } 264