Blanket copy of PhoneApp to services/Telephony.
First phase of splitting out InCallUI from PhoneApp.
Change-Id: I237341c4ff00e96c677caa4580b251ef3432931b
diff --git a/src/com/android/phone/SpecialCharSequenceMgr.java b/src/com/android/phone/SpecialCharSequenceMgr.java
new file mode 100644
index 0000000..9b5373c
--- /dev/null
+++ b/src/com/android/phone/SpecialCharSequenceMgr.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.Phone;
+import android.telephony.PhoneNumberUtils;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.internal.telephony.TelephonyCapabilities;
+
+/**
+ * Helper class to listen for some magic dialpad character sequences
+ * that are handled specially by the Phone app.
+ *
+ * Note the Contacts app also handles these sequences too, so there's a
+ * separate version of this class under apps/Contacts.
+ *
+ * In fact, the most common use case for these special sequences is typing
+ * them from the regular "Dialer" used for outgoing calls, which is part
+ * of the contacts app; see DialtactsActivity and DialpadFragment.
+ * *This* version of SpecialCharSequenceMgr is used for only a few
+ * relatively obscure places in the UI:
+ * - The "SIM network unlock" PIN entry screen (see
+ * IccNetworkDepersonalizationPanel.java)
+ * - The emergency dialer (see EmergencyDialer.java).
+ *
+ * TODO: there's lots of duplicated code between this class and the
+ * corresponding class under apps/Contacts. Let's figure out a way to
+ * unify these two classes (in the framework? in a common shared library?)
+ */
+public class SpecialCharSequenceMgr {
+ private static final String TAG = PhoneGlobals.LOG_TAG;
+ private static final boolean DBG = false;
+
+ private static final String MMI_IMEI_DISPLAY = "*#06#";
+ private static final String MMI_REGULATORY_INFO_DISPLAY = "*#07#";
+
+ /** This class is never instantiated. */
+ private SpecialCharSequenceMgr() {
+ }
+
+ /**
+ * Check for special strings of digits from an input
+ * string.
+ * @param context input Context for the events we handle.
+ * @param input the dial string to be examined.
+ */
+ static boolean handleChars(Context context, String input) {
+ return handleChars(context, input, null);
+ }
+
+ /**
+ * Generally used for the Personal Unblocking Key (PUK) unlocking
+ * case, where we want to be able to maintain a handle to the
+ * calling activity so that we can close it or otherwise display
+ * indication if the PUK code is recognized.
+ *
+ * NOTE: The counterpart to this file in Contacts does
+ * NOT contain the special PUK handling code, since it
+ * does NOT need it. When the device gets into PUK-
+ * locked state, the keyguard comes up and the only way
+ * to unlock the device is through the Emergency dialer,
+ * which is still in the Phone App.
+ *
+ * @param context input Context for the events we handle.
+ * @param input the dial string to be examined.
+ * @param pukInputActivity activity that originated this
+ * PUK call, tracked so that we can close it or otherwise
+ * indicate that special character sequence is
+ * successfully processed. Can be null.
+ * @return true if the input was a special string which has been
+ * handled.
+ */
+ static boolean handleChars(Context context,
+ String input,
+ Activity pukInputActivity) {
+
+ //get rid of the separators so that the string gets parsed correctly
+ String dialString = PhoneNumberUtils.stripSeparators(input);
+
+ if (handleIMEIDisplay(context, dialString)
+ || handleRegulatoryInfoDisplay(context, dialString)
+ || handlePinEntry(context, dialString, pukInputActivity)
+ || handleAdnEntry(context, dialString)
+ || handleSecretCode(context, dialString)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Variant of handleChars() that looks for the subset of "special
+ * sequences" that are available even if the device is locked.
+ *
+ * (Specifically, these are the sequences that you're allowed to type
+ * in the Emergency Dialer, which is accessible *without* unlocking
+ * the device.)
+ */
+ static boolean handleCharsForLockedDevice(Context context,
+ String input,
+ Activity pukInputActivity) {
+ // Get rid of the separators so that the string gets parsed correctly
+ String dialString = PhoneNumberUtils.stripSeparators(input);
+
+ // The only sequences available on a locked device are the "**04"
+ // or "**05" sequences that allow you to enter PIN or PUK-related
+ // codes. (e.g. for the case where you're currently locked out of
+ // your phone, and need to change the PIN! The only way to do
+ // that is via the Emergency Dialer.)
+
+ if (handlePinEntry(context, dialString, pukInputActivity)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*.
+ * If a secret code is encountered an Intent is started with the android_secret_code://<code>
+ * URI.
+ *
+ * @param context the context to use
+ * @param input the text to check for a secret code in
+ * @return true if a secret code was encountered
+ */
+ static private boolean handleSecretCode(Context context, String input) {
+ // Secret codes are in the form *#*#<code>#*#*
+ int len = input.length();
+ if (len > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
+ Intent intent = new Intent(TelephonyIntents.SECRET_CODE_ACTION,
+ Uri.parse("android_secret_code://" + input.substring(4, len - 4)));
+ context.sendBroadcast(intent);
+ return true;
+ }
+
+ return false;
+ }
+
+ static private boolean handleAdnEntry(Context context, String input) {
+ /* ADN entries are of the form "N(N)(N)#" */
+
+ // if the phone is keyguard-restricted, then just ignore this
+ // input. We want to make sure that sim card contacts are NOT
+ // exposed unless the phone is unlocked, and this code can be
+ // accessed from the emergency dialer.
+ if (PhoneGlobals.getInstance().getKeyguardManager().inKeyguardRestrictedInputMode()) {
+ return false;
+ }
+
+ int len = input.length();
+ if ((len > 1) && (len < 5) && (input.endsWith("#"))) {
+ try {
+ int index = Integer.parseInt(input.substring(0, len-1));
+ Intent intent = new Intent(Intent.ACTION_PICK);
+
+ intent.setClassName("com.android.phone",
+ "com.android.phone.SimContacts");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra("index", index);
+ PhoneGlobals.getInstance().startActivity(intent);
+
+ return true;
+ } catch (NumberFormatException ex) {}
+ }
+ return false;
+ }
+
+ static private boolean handlePinEntry(Context context, String input,
+ Activity pukInputActivity) {
+ // TODO: The string constants here should be removed in favor
+ // of some call to a static the MmiCode class that determines
+ // if a dialstring is an MMI code.
+ if ((input.startsWith("**04") || input.startsWith("**05"))
+ && input.endsWith("#")) {
+ PhoneGlobals app = PhoneGlobals.getInstance();
+ boolean isMMIHandled = app.phone.handlePinMmi(input);
+
+ // if the PUK code is recognized then indicate to the
+ // phone app that an attempt to unPUK the device was
+ // made with this activity. The PUK code may still
+ // fail though, but we won't know until the MMI code
+ // returns a result.
+ if (isMMIHandled && input.startsWith("**05")) {
+ app.setPukEntryActivity(pukInputActivity);
+ }
+ return isMMIHandled;
+ }
+ return false;
+ }
+
+ static private boolean handleIMEIDisplay(Context context,
+ String input) {
+ if (input.equals(MMI_IMEI_DISPLAY)) {
+ showDeviceIdPanel(context);
+ return true;
+ }
+
+ return false;
+ }
+
+ static private void showDeviceIdPanel(Context context) {
+ if (DBG) log("showDeviceIdPanel()...");
+
+ Phone phone = PhoneGlobals.getPhone();
+ int labelId = TelephonyCapabilities.getDeviceIdLabel(phone);
+ String deviceId = phone.getDeviceId();
+
+ AlertDialog alert = new AlertDialog.Builder(context)
+ .setTitle(labelId)
+ .setMessage(deviceId)
+ .setPositiveButton(R.string.ok, null)
+ .setCancelable(false)
+ .create();
+ alert.getWindow().setType(WindowManager.LayoutParams.TYPE_PRIORITY_PHONE);
+ alert.show();
+ }
+
+ private static boolean handleRegulatoryInfoDisplay(Context context, String input) {
+ if (input.equals(MMI_REGULATORY_INFO_DISPLAY)) {
+ log("handleRegulatoryInfoDisplay() sending intent to settings app");
+ ComponentName regInfoDisplayActivity = new ComponentName(
+ "com.android.settings", "com.android.settings.RegulatoryInfoDisplayActivity");
+ Intent showRegInfoIntent = new Intent("android.settings.SHOW_REGULATORY_INFO");
+ showRegInfoIntent.setComponent(regInfoDisplayActivity);
+ try {
+ context.startActivity(showRegInfoIntent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "startActivity() failed: " + e);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, "[SpecialCharSequenceMgr] " + msg);
+ }
+}