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.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