Home | History | Annotate | Download | only in phone
      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