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