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.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. */
     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      */
     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      */
     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      */
    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      */
    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 
    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 
    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 
    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 
    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 
    237     private static void log(String msg) {
    238         Log.d(TAG, "[SpecialCharSequenceMgr] " + msg);
    239     }
    240 }
    241