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