am d2ad6183: am 1d154f78: Merge "HFA support in services/Telephony for provisioning CDMA devices." into klp-dev

* commit 'd2ad61832a77694adf80067645244716921c0602':
  HFA support in services/Telephony for provisioning CDMA devices.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6ed3dc7..fe71d4c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -513,6 +513,12 @@
                 android:theme="@style/Theme.Transparent">
         </activity>
 
+        <activity android:name="HfaActivity"
+                android:excludeFromRecents="true"
+                android:launchMode="singleInstance"
+                android:theme="@style/Empty">
+        </activity>
+
         <receiver android:name="CallerInfoCacheUpdateReceiver">
             <intent-filter>
                 <action android:name="com.android.phone.UPDATE_CALLER_INFO_CACHE" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 2d3317c..adc54ee 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -125,4 +125,7 @@
 
     <!-- Class name for the default in-call UI Service [DO NOT TRANSLATE] -->
     <string name="incall_ui_default_class" translatable="false">com.android.incallui.CallHandlerService</string>
+
+    <!-- CDMA activation goes through HFA [DO NOT TRANSLATE] -->
+    <bool name="config_use_hfa_for_provisioning" translatable="false">false</bool>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1e5122e..f644dda 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -935,7 +935,10 @@
     <!-- Message displayed on the OTA activation screen. -->
     <string name="ota_touch_activate">A special call needs to be made to activate your phone service.
     \n\nAfter pressing \u201CActivate\u201D, listen to the instructions provided to activate your phone.</string>
-
+    <!-- Title shown during Hands Free Activation -->
+    <string name="ota_hfa_activation_title">Activating...</string>
+    <!-- Message shown during the hands free activation screen of the setup wizard. -->
+    <string name="ota_hfa_activation_dialog_message">The phone is activating your mobile data service.\n\nThis can take up to 5 minutes.</string>
     <!-- Title of skip activation dialog -->
     <string name="ota_skip_activation_dialog_title">Skip activation\?</string>
     <!-- Message displayed in skip activation dialog  -->
diff --git a/src/com/android/phone/HfaActivity.java b/src/com/android/phone/HfaActivity.java
new file mode 100644
index 0000000..3bc047d
--- /dev/null
+++ b/src/com/android/phone/HfaActivity.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2013 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.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.ServiceState;
+import android.util.Log;
+
+import com.android.internal.telephony.CallManager;
+import com.android.internal.telephony.Phone;
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+
+/**
+ * Starts and displays status for Hands Free Activation (HFA).
+ *
+ * This class operates with Hands Free Activation apps.
+ * It starts by broadcasting the intent com.android.action.START_HFA.
+ * An HFA app will pick that up and start the HFA process.
+ * If it fails it return ERROR_HFA Intent and upon success returns COMPLETE_HFA.
+ *
+ * If successful, we bounce the radio so that the service picks up the new number. This is also
+ * necessary for the setup wizard to pick up the successful activation so that it can continue
+ * past the welcome screen. Once the radio is back on we send back the pendingIntent to setup
+ * wizard and destroy the activity.
+ *
+ * If there is an error, we do not bounce the radio but still send the pending intent back to
+ * the wizard (with a failure code).
+ *
+ * The user has an option to skip activation. If that happens, we go back to the setup
+ * wizard.
+ *
+ * TODO(klp): We need system-only permissions for the HFA intents.
+ * TODO(klp): Should be full screen activity instead of dialogs.
+ * TODO(klp): Currently display the error code instead of the error string resource.
+ * TODO(klp): Need to check the system to ensure someone is listening for the intent
+ *            before we send it.  Should there be a timeout?  5 minutes?
+ */
+public class HfaActivity extends Activity {
+    private static final String TAG = HfaActivity.class.getSimpleName();
+
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final String ACTION_START = "com.android.action.START_HFA";
+    private static final String ACTION_ERROR = "com.android.action.ERROR_HFA";
+    private static final String ACTION_CANCEL = "com.android.action.CANCEL_HFA";
+    private static final String ACTION_COMPLETE = "com.android.action.COMPLETE_HFA";
+
+    private static final int SERVICE_STATE_CHANGED = 1;
+
+    public static final int OTASP_UNKNOWN = 0;
+    public static final int OTASP_USER_SKIPPED = 1;
+    public static final int OTASP_SUCCESS = 2;
+    public static final int OTASP_FAILURE = 3;
+
+    public static final int NOT_WAITING = 0;
+    public static final int WAITING_FOR_RADIO_OFF = 1;
+    public static final int WAITING_FOR_RADIO_ON = 2;
+
+    private int mPhoneMonitorState = NOT_WAITING;
+    private boolean mCanSkip;
+    private AlertDialog mDialog;
+    private BroadcastReceiver mReceiver;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (VERBOSE) Log.v(TAG, "onCreate");
+
+        startHfaIntentReceiver();
+        startProvisioning();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        if (VERBOSE) Log.v(TAG, "onDestroy");
+
+        if (mDialog != null && mDialog.isShowing()) {
+            mDialog.dismiss();
+            mDialog = null;
+        }
+
+        if (mReceiver != null) {
+            unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+    }
+
+    private void startProvisioning() {
+        buildAndShowDialog();
+
+        sendHfaCommand(ACTION_START);
+    }
+
+    private void buildAndShowDialog() {
+        mCanSkip = true;
+
+        mDialog = new AlertDialog.Builder(this)
+                .setTitle(R.string.ota_hfa_activation_title)
+                .setMessage(R.string.ota_hfa_activation_dialog_message)
+                .setPositiveButton(R.string.ota_skip_activation_dialog_skip_label,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface di, int which) {
+                                if (mCanSkip) {
+                                    sendHfaCommand(ACTION_CANCEL);
+                                    sendResponseToSetupWizard(OTASP_USER_SKIPPED);
+                                }
+                            }})
+                /*.setOnCancelListener(new DialogInterface.OnCancelListener() {
+                    @Override
+                    public void onCancel(DialogInterface di) {
+                        sendResponseToSetupWizard(OTASP_USER_SKIPPED);
+                    }})*/
+                .create();
+
+        if (VERBOSE) Log.v(TAG, "showing dialog");
+        mDialog.show();
+    }
+
+    private void sendHfaCommand(String action) {
+        if (VERBOSE) Log.v(TAG, "Sending command: " + action);
+        sendBroadcast(new Intent(action));
+    }
+
+    private void onHfaError(String errorMsg) {
+        mDialog.dismiss();
+
+        AlertDialog errorDialog = new AlertDialog.Builder(this)
+            .setMessage(errorMsg)
+            .setPositiveButton(R.string.ota_skip_activation_dialog_skip_label,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface di, int which) {
+                            di.dismiss();
+                            sendResponseToSetupWizard(OTASP_USER_SKIPPED);
+                        }
+                    })
+            .setNegativeButton(R.string.ota_try_again,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface di, int which) {
+                            di.dismiss();
+                            startProvisioning();
+                        }
+                    })
+            .create();
+
+        errorDialog.show();
+    }
+
+    private void onHfaSuccess() {
+        // User can no longer skip after success.
+        mCanSkip = false;
+
+        // We need to restart the modem upon successful activation
+        // so that it can acquire a number and ensure setupwizard will
+        // know about this success through phone state changes.
+
+        bounceRadio();
+    }
+
+    private void bounceRadio() {
+        final Phone phone = PhoneGlobals.getInstance().getPhone();
+        phone.registerForServiceStateChanged(mHandler, SERVICE_STATE_CHANGED, null);
+
+        mPhoneMonitorState = WAITING_FOR_RADIO_OFF;
+        phone.setRadioPower(false);
+        onServiceStateChange(phone.getServiceState());
+    }
+
+    private void onServiceStateChange(ServiceState state) {
+        final boolean radioIsOff = state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
+        final Phone phone = PhoneGlobals.getInstance().getPhone();
+
+        if (VERBOSE) Log.v(TAG, "Radio is off: " + radioIsOff);
+
+        if (mPhoneMonitorState == WAITING_FOR_RADIO_OFF) {
+            if (radioIsOff) {
+                mPhoneMonitorState = WAITING_FOR_RADIO_ON;
+                phone.setRadioPower(true);
+            }
+        } else if (mPhoneMonitorState == WAITING_FOR_RADIO_ON) {
+            if (!radioIsOff) {
+                mPhoneMonitorState = NOT_WAITING;
+                phone.unregisterForServiceStateChanged(mHandler);
+
+                // We have successfully bounced the radio.
+                // Time to go back to the setup wizard.
+                sendResponseToSetupWizard(OTASP_SUCCESS);
+            }
+        }
+    }
+
+    private void sendResponseToSetupWizard(int responseCode) {
+        final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
+                OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
+
+        final Intent extraStuff = new Intent();
+        extraStuff.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE, responseCode);
+
+        try {
+            if (VERBOSE) Log.v(TAG, "Sending OTASP confirmation with result code: " + responseCode);
+            otaResponseIntent.send(this, 0 /* resultCode (not used) */, extraStuff);
+        } catch (CanceledException e) {
+            Log.e(TAG, "Pending Intent canceled");
+        }
+
+        finish();
+    }
+
+    public void startHfaIntentReceiver() {
+        final IntentFilter filter = new IntentFilter(ACTION_COMPLETE);
+        filter.addAction(ACTION_ERROR);
+
+        mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                if (action.equals(ACTION_ERROR)) {
+                    onHfaError(intent.getStringExtra("errorCode"));
+                } else if (action.equals(ACTION_COMPLETE)) {
+                    if (VERBOSE) Log.v(TAG, "Hfa Successful");
+                    onHfaSuccess();
+                }
+            }
+        };
+
+        registerReceiver(mReceiver, filter);
+    }
+
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SERVICE_STATE_CHANGED:
+                    ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
+                    onServiceStateChange(state);
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+}
diff --git a/src/com/android/phone/InCallScreenShowActivation.java b/src/com/android/phone/InCallScreenShowActivation.java
index 243b437..15abb11 100644
--- a/src/com/android/phone/InCallScreenShowActivation.java
+++ b/src/com/android/phone/InCallScreenShowActivation.java
@@ -67,6 +67,14 @@
 
         if (intent.getAction().equals(OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING)) {
 
+            boolean usesHfa = getResources().getBoolean(R.bool.config_use_hfa_for_provisioning);
+            if (usesHfa) {
+                Log.d(LOG_TAG, "Starting Hfa from ACTION_PERFORM_CDMA_PROVISIONING");
+                startHfa();
+                finish();
+                return;
+            }
+
             // On voice-capable devices, we perform CDMA provisioning in
             // "interactive" mode by directly launching the InCallScreen.
             // boolean interactiveMode = PhoneGlobals.sVoiceCapable;
@@ -137,4 +145,21 @@
 
         finish();
     }
+
+
+    /**
+     * Starts the HFA provisioning process by bringing up the HFA Activity.
+     */
+    private void startHfa() {
+        final Intent intent = new Intent(this, HfaActivity.class);
+
+        final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
+                OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
+
+        intent.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT, otaResponseIntent);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+        Log.v(LOG_TAG, "Starting hfa activation activity");
+        startActivity(intent);
+    }
 }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index db06c30..45ffd33 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -1588,4 +1588,5 @@
             mBluetoothPhone = null;
         }
     };
+
 }