Merge "Send telephony notifications to appropriate users." into lmp-dev
diff --git a/res/values-mcc311-mnc220/config.xml b/res/values-mcc311-mnc220/config.xml
index 7c269e0..22701f0 100644
--- a/res/values-mcc311-mnc220/config.xml
+++ b/res/values-mcc311-mnc220/config.xml
@@ -19,4 +19,6 @@
 <resources>
     <!-- Flag indicating if dtmf tone type is enabled -->
     <bool name="dtmf_type_enabled">true</bool>
+    <!-- CDMA activation goes through OTASP. -->
+    <bool name="config_use_otasp_for_provisioning">true</bool>
 </resources>
diff --git a/res/values-mcc311-mnc580/config.xml b/res/values-mcc311-mnc580/config.xml
index 7c269e0..22701f0 100644
--- a/res/values-mcc311-mnc580/config.xml
+++ b/res/values-mcc311-mnc580/config.xml
@@ -19,4 +19,6 @@
 <resources>
     <!-- Flag indicating if dtmf tone type is enabled -->
     <bool name="dtmf_type_enabled">true</bool>
+    <!-- CDMA activation goes through OTASP. -->
+    <bool name="config_use_otasp_for_provisioning">true</bool>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 3daf89a..cd82234 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -127,8 +127,13 @@
     <!-- Class name for the default main Dialer activity [DO NOT TRANSLATE] -->
     <string name="dialer_default_class" translatable="false">com.android.dialer.DialtactsActivity</string>
 
-    <!-- CDMA activation goes through HFA [DO NOT TRANSLATE] -->
-    <bool name="config_use_hfa_for_provisioning" translatable="false">false</bool>
+    <!-- CDMA activation goes through HFA -->
+    <bool name="config_use_hfa_for_provisioning">false</bool>
+
+    <!-- CDMA activation goes through OTASP.
+        TODO: This should be combined with config_use_hfa_for_provisioning and implemented
+        as an enum (NONE, HFA, OTASP). -->
+    <bool name="config_use_otasp_for_provisioning">false</bool>
 
     <!-- Display carrier settings menu if true -->
     <bool name="config_carrier_settings_enable">false</bool>
@@ -141,4 +146,7 @@
 
     <!-- Show APN Settings for some CDMA carriers -->
     <bool name="config_show_apn_setting_cdma">false</bool>
+
+    <!-- Allows the telephony HFA logic to run even if we're not in setup wizard. -->
+    <bool name="config_allow_hfa_outside_of_setup_wizard">true</bool>
 </resources>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index b85cb7e..c38df5c 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -485,7 +485,10 @@
             mAudioManager.setParameter(HAC_KEY, hac != 0 ? HAC_VAL_ON : HAC_VAL_OFF);
             return true;
         } else if (preference == mVoicemailSettings) {
-            mVoicemailSettings.getDialog().getActionBar().setDisplayHomeAsUpEnabled(false);
+            final Dialog dialog = mVoicemailSettings.getDialog();
+            if (dialog != null) {
+                dialog.getActionBar().setDisplayHomeAsUpEnabled(false);
+            }
             if (DBG) log("onPreferenceTreeClick: Voicemail Settings Preference is clicked.");
             if (preference.getIntent() != null) {
                 if (DBG) {
@@ -511,7 +514,10 @@
                 return false;
             }
         } else if (preference == mVoicemailSettingsScreen) {
-            mVoicemailSettingsScreen.getDialog().getActionBar().setDisplayHomeAsUpEnabled(false);
+            final Dialog dialog = mVoicemailSettingsScreen.getDialog();
+            if (dialog != null) {
+                dialog.getActionBar().setDisplayHomeAsUpEnabled(false);
+            }
             return false;
         }
         return false;
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index be1e3b2..f22913c 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -142,8 +142,6 @@
 
         callStateMonitor.addListener(this);
 
-        createSignalInfoToneGenerator();
-
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter != null) {
             adapter.getProfileProxy(mApplication.getApplicationContext(),
@@ -944,7 +942,7 @@
         @Override
         public void run() {
             log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
-
+            createSignalInfoToneGenerator();
             if (mSignalInfoToneGenerator != null) {
                 //First stop any ongoing SignalInfo tone
                 mSignalInfoToneGenerator.stopTone();
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index 921b7f7..7f4bd1b 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -50,6 +50,8 @@
  */
 public class EmergencyCallbackModeExitDialog extends Activity implements OnDismissListener {
 
+    private static final String TAG = "EmergencyCallbackMode";
+
     /** Intent to trigger the Emergency Callback Mode exit dialog */
     static final String ACTION_SHOW_ECM_EXIT_DIALOG =
             "com.android.phone.action.ACTION_SHOW_ECM_EXIT_DIALOG";
@@ -77,9 +79,12 @@
         super.onCreate(savedInstanceState);
 
         // Check if phone is in Emergency Callback Mode. If not, exit.
-        if (!Boolean.parseBoolean(
-                    SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
+        final boolean isInEcm = Boolean.parseBoolean(
+                SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE));
+        Log.i(TAG, "ECMModeExitDialog launched - isInEcm: " + isInEcm);
+        if (!isInEcm) {
             finish();
+            return;
         }
 
         mHandler = new Handler();
@@ -103,9 +108,15 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        unregisterReceiver(mEcmExitReceiver);
+        try {
+            unregisterReceiver(mEcmExitReceiver);
+        } catch (IllegalArgumentException e) {
+            // Receiver was never registered - silently ignore.
+        }
         // Unregister ECM timer reset notification
-        mPhone.unregisterForEcmTimerReset(mHandler);
+        if (mPhone != null) {
+            mPhone.unregisterForEcmTimerReset(mHandler);
+        }
     }
 
     @Override
@@ -148,11 +159,16 @@
             if (mService != null) {
                 mEcmTimeout = mService.getEmergencyCallbackModeTimeout();
                 mInEmergencyCall = mService.getEmergencyCallbackModeCallState();
+                try {
+                    // Unbind from remote service
+                    unbindService(mConnection);
+                } catch (IllegalArgumentException e) {
+                    // Failed to unbind from service. Don't crash as this brings down the entire
+                    // radio.
+                    Log.w(TAG, "Failed to unbind from EmergencyCallbackModeService");
+                }
             }
 
-            // Unbind from remote service
-            unbindService(mConnection);
-
             // Show dialog
             mHandler.post(new Runnable() {
                 public void run() {
@@ -166,7 +182,10 @@
      * Shows Emergency Callback Mode dialog and starts countdown timer
      */
     private void showEmergencyCallbackModeExitDialog() {
-
+        if (!this.isResumed()) {
+            Log.w(TAG, "Tried to show dialog, but activity was already finished");
+            return;
+        }
         if(mInEmergencyCall) {
             mDialogType = EXIT_ECM_IN_EMERGENCY_CALL_DIALOG;
             showDialog(EXIT_ECM_IN_EMERGENCY_CALL_DIALOG);
@@ -283,6 +302,7 @@
     /**
      * Closes activity when dialog is dismissed
      */
+    @Override
     public void onDismiss(DialogInterface dialog) {
         EmergencyCallbackModeExitDialog.this.setResult(RESULT_OK, (new Intent())
                 .putExtra(EXTRA_EXIT_ECM_RESULT, false));
diff --git a/src/com/android/phone/EmergencyCallbackModeService.java b/src/com/android/phone/EmergencyCallbackModeService.java
index e1f7fb3..3310df1 100644
--- a/src/com/android/phone/EmergencyCallbackModeService.java
+++ b/src/com/android/phone/EmergencyCallbackModeService.java
@@ -144,26 +144,40 @@
         showNotification(ecmTimeout);
 
         // Start countdown timer for the notification updates
-        mTimer = new CountDownTimer(ecmTimeout, 1000) {
+        if (mTimer != null) {
+            mTimer.cancel();
+        } else {
+            mTimer = new CountDownTimer(ecmTimeout, 1000) {
 
-            @Override
-            public void onTick(long millisUntilFinished) {
-                mTimeLeft = millisUntilFinished;
-                EmergencyCallbackModeService.this.showNotification(millisUntilFinished);
-            }
+                @Override
+                public void onTick(long millisUntilFinished) {
+                    mTimeLeft = millisUntilFinished;
+                    EmergencyCallbackModeService.this.showNotification(millisUntilFinished);
+                }
 
-            @Override
-            public void onFinish() {
-                //Do nothing
-            }
+                @Override
+                public void onFinish() {
+                    //Do nothing
+                }
 
-        }.start();
+            };
+        }
+        mTimer.start();
     }
 
     /**
      * Shows notification for Emergency Callback Mode
      */
     private void showNotification(long millisUntilFinished) {
+        final boolean isInEcm = Boolean.parseBoolean(
+                SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE));
+        if (!isInEcm) {
+            Log.i(LOG_TAG, "Asked to show notification but not in ECM mode");
+            if (mTimer != null) {
+                mTimer.cancel();
+            }
+            return;
+        }
         final Notification.Builder builder = new Notification.Builder(getApplicationContext());
         builder.setOngoing(true);
         builder.setPriority(Notification.PRIORITY_HIGH);
diff --git a/src/com/android/phone/InCallScreenShowActivation.java b/src/com/android/phone/InCallScreenShowActivation.java
index fd202db..34710a1 100644
--- a/src/com/android/phone/InCallScreenShowActivation.java
+++ b/src/com/android/phone/InCallScreenShowActivation.java
@@ -73,74 +73,81 @@
 
             boolean usesHfa = getResources().getBoolean(R.bool.config_use_hfa_for_provisioning);
             if (usesHfa) {
-                Log.d(LOG_TAG, "Starting Hfa from ACTION_PERFORM_CDMA_PROVISIONING");
+                Log.i(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;
-            // TODO: Renable interactive mode for device provisioning.
-            boolean interactiveMode = false;
-            Log.d(LOG_TAG, "ACTION_PERFORM_CDMA_PROVISIONING (interactiveMode = "
-                  + interactiveMode + ")...");
+            boolean usesOtasp = getResources().getBoolean(R.bool.config_use_otasp_for_provisioning);
+            if (usesOtasp) {
+                // On voice-capable devices, we perform CDMA provisioning in
+                // "interactive" mode by directly launching the InCallScreen.
+                // boolean interactiveMode = PhoneGlobals.sVoiceCapable;
+                // TODO: Renable interactive mode for device provisioning.
+                boolean interactiveMode = false;
+                Log.i(LOG_TAG, "ACTION_PERFORM_CDMA_PROVISIONING (interactiveMode = "
+                      + interactiveMode + ")...");
 
-            // Testing: this intent extra allows test apps manually
-            // enable/disable "interactive mode", regardless of whether
-            // the current device is voice-capable.  This is allowed only
-            // in userdebug or eng builds.
-            if (intent.hasExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE)
-                    && (SystemProperties.getInt("ro.debuggable", 0) == 1)) {
-                interactiveMode =
-                        intent.getBooleanExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE, false);
-                Log.d(LOG_TAG, "===> MANUALLY OVERRIDING interactiveMode to " + interactiveMode);
-            }
-
-            // We allow the caller to pass a PendingIntent (as the
-            // EXTRA_NONINTERACTIVE_OTASP_RESULT_PENDING_INTENT extra)
-            // which we'll later use to notify them when the OTASP call
-            // fails or succeeds.
-            //
-            // Stash that away here, and we'll fire it off later in
-            // OtaUtils.sendOtaspResult().
-            app.cdmaOtaScreenState.otaspResultCodePendingIntent =
-                        (PendingIntent) intent.getParcelableExtra(
-                                OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
-
-            if (interactiveMode) {
-                // On voice-capable devices, launch an OTASP call and arrange
-                // for the in-call UI to come up.  (The InCallScreen will
-                // notice that an OTASP call is active, and display the
-                // special OTASP UI instead of the usual in-call controls.)
-
-                if (DBG) Log.d(LOG_TAG, "==> Starting interactive CDMA provisioning...");
-                OtaUtils.startInteractiveOtasp(this);
-
-                // The result we set here is actually irrelevant, since the
-                // InCallScreen's "interactive" OTASP sequence never actually
-                // finish()es; it ends by directly launching the Home
-                // activity.  So our caller won't actually ever get an
-                // onActivityResult() call in this case.
-                setResult(OtaUtils.RESULT_INTERACTIVE_OTASP_STARTED);
-            } else {
-                // On data-only devices, manually launch the OTASP call
-                // *without* displaying any UI.  (Our caller, presumably
-                // SetupWizardActivity, is responsible for displaying some
-                // sort of progress UI.)
-
-                if (DBG) Log.d(LOG_TAG, "==> Starting non-interactive CDMA provisioning...");
-                int callStatus = OtaUtils.startNonInteractiveOtasp(this);
-
-                if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
-                    if (DBG) Log.d(LOG_TAG, "  ==> successful result from startNonInteractiveOtasp(): "
-                          + callStatus);
-                    setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_STARTED);
-                } else {
-                    Log.w(LOG_TAG, "Failure code from startNonInteractiveOtasp(): " + callStatus);
-                    setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_FAILED);
+                // Testing: this intent extra allows test apps manually
+                // enable/disable "interactive mode", regardless of whether
+                // the current device is voice-capable.  This is allowed only
+                // in userdebug or eng builds.
+                if (intent.hasExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE)
+                        && (SystemProperties.getInt("ro.debuggable", 0) == 1)) {
+                    interactiveMode =
+                            intent.getBooleanExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE, false);
+                    Log.d(LOG_TAG, "==> MANUALLY OVERRIDING interactiveMode to " + interactiveMode);
                 }
+
+                // We allow the caller to pass a PendingIntent (as the
+                // EXTRA_NONINTERACTIVE_OTASP_RESULT_PENDING_INTENT extra)
+                // which we'll later use to notify them when the OTASP call
+                // fails or succeeds.
+                //
+                // Stash that away here, and we'll fire it off later in
+                // OtaUtils.sendOtaspResult().
+                app.cdmaOtaScreenState.otaspResultCodePendingIntent =
+                            (PendingIntent) intent.getParcelableExtra(
+                                    OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
+
+                if (interactiveMode) {
+                    // On voice-capable devices, launch an OTASP call and arrange
+                    // for the in-call UI to come up.  (The InCallScreen will
+                    // notice that an OTASP call is active, and display the
+                    // special OTASP UI instead of the usual in-call controls.)
+
+                    if (DBG) Log.d(LOG_TAG, "==> Starting interactive CDMA provisioning...");
+                    OtaUtils.startInteractiveOtasp(this);
+
+                    // The result we set here is actually irrelevant, since the
+                    // InCallScreen's "interactive" OTASP sequence never actually
+                    // finish()es; it ends by directly launching the Home
+                    // activity.  So our caller won't actually ever get an
+                    // onActivityResult() call in this case.
+                    setResult(OtaUtils.RESULT_INTERACTIVE_OTASP_STARTED);
+                } else {
+                    // On data-only devices, manually launch the OTASP call
+                    // *without* displaying any UI.  (Our caller, presumably
+                    // SetupWizardActivity, is responsible for displaying some
+                    // sort of progress UI.)
+
+                    if (DBG) Log.d(LOG_TAG, "==> Starting non-interactive CDMA provisioning...");
+                    int callStatus = OtaUtils.startNonInteractiveOtasp(this);
+
+                    if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
+                        if (DBG) Log.d(LOG_TAG,
+                                "  ==> successful result from startNonInteractiveOtasp(): " +
+                                callStatus);
+                        setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_STARTED);
+                    } else {
+                        Log.w(LOG_TAG, "Failure code from startNonInteractiveOtasp(): " +
+                                callStatus);
+                        setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_FAILED);
+                    }
+                }
+            } else {
+                Log.i(LOG_TAG, "Skipping activation.");
             }
         } else {
             Log.e(LOG_TAG, "Unexpected intent action: " + intent);
@@ -180,28 +187,34 @@
      * Starts the HFA provisioning process by bringing up the HFA Activity.
      */
     private void startHfa() {
-        final Intent intent = new Intent();
+        boolean isWizardRunning = isWizardRunning(this);
+        // We always run our HFA logic if we're in setup wizard, but if we're outside of setup
+        // wizard then we have to check a config to see if we should still run HFA.
+        if (isWizardRunning ||
+                getResources().getBoolean(R.bool.config_allow_hfa_outside_of_setup_wizard)) {
 
-        final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
-                OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
+            final Intent intent = new Intent();
 
-        final boolean showUi = !isWizardRunning(this);
+            final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
+                    OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
 
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            final boolean showUi = !isWizardRunning;
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        if (otaResponseIntent != null) {
-            intent.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT, otaResponseIntent);
+            if (otaResponseIntent != null) {
+                intent.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT, otaResponseIntent);
+            }
+
+            Log.v(LOG_TAG, "Starting hfa activation activity");
+            if (showUi) {
+                intent.setClassName(this, HfaActivity.class.getName());
+                startActivity(intent);
+            } else {
+                intent.setClassName(this, HfaService.class.getName());
+                startService(intent);
+            }
+
         }
-
-        Log.v(LOG_TAG, "Starting hfa activation activity");
-        if (showUi) {
-            intent.setClassName(this, HfaActivity.class.getName());
-            startActivity(intent);
-        } else {
-            intent.setClassName(this, HfaService.class.getName());
-            startService(intent);
-        }
-
         setResult(RESULT_OK);
     }
 }
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 654150c..f4c8a22 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -197,6 +197,10 @@
 
     private void handleNewUnknownConnection(AsyncResult asyncResult) {
         Log.i(this, "handleNewUnknownConnection");
+        if (!(asyncResult.result instanceof Connection)) {
+            Log.w(this, "handleNewUnknownConnection called with non-Connection object");
+            return;
+        }
         Connection connection = (Connection) asyncResult.result;
         if (connection != null) {
             Call call = connection.getCall();
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index e8c148a..73a57d3 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -84,6 +84,13 @@
             // TODO: We don't check for SecurityException here (requires
             // CALL_PRIVILEGED permission).
             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
+            if (phone == null) {
+                Log.d(this, "onCreateOutgoingConnection, phone is null");
+                return Connection.createFailedConnection(
+                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                                android.telephony.DisconnectCause.OUTGOING_FAILURE,
+                                "Phone is null"));
+            }
             number = phone.getVoiceMailNumber();
             if (TextUtils.isEmpty(number)) {
                 Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");