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