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