Merge "Fix issue where device does not create any phone accounts."
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 25018a3..564c832 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -561,8 +561,13 @@
          lose data connectivity because you're roaming and you have the
          "data roaming" feature turned off. -->
     <string name="roaming_reenable_message">Data roaming is turned off. Tap to turn on.</string>
+    <!-- Mobile network settings UI: notification message shown when you
+         are roaming and you have the "data roaming" feature turned on. -->
+    <string name="roaming_enabled_message">Roaming charges may apply. Tap to modify.</string>
     <!-- Roaming notification tile, notifying lost of roaming data connection -->
     <string name="roaming_notification_title">Lost mobile data connection</string>
+    <!-- Roaming notification tile, notifying continued roaming data connection -->
+    <string name="roaming_on_notification_title">Data roaming is on</string>
     <!-- Mobile network settings screen, dialog message when user selects the Data roaming check box -->
     <string name="roaming_warning">You may incur significant charges.</string>
     <!-- Mobile network settings screen, message asking the user to check their pricing with their Carrier, when enabling Data roaming. -->
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 3796e64..f9013c6 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -392,7 +392,7 @@
             try {
                 int stream;
                 if (mBluetoothHeadset != null) {
-                    stream = mBluetoothHeadset.isAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO:
+                    stream = isBluetoothAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO :
                         AudioManager.STREAM_VOICE_CALL;
                 } else {
                     stream = AudioManager.STREAM_VOICE_CALL;
@@ -479,6 +479,11 @@
         }
     }
 
+    // Returns whether there are any connected Bluetooth audio devices
+    private boolean isBluetoothAudioOn() {
+        return mBluetoothHeadset.getConnectedDevices().size() > 0;
+    }
+
     /**
      * Displays a notification when the phone receives a DisplayInfo record.
      */
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index ca99f98..5a5b444 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
@@ -90,7 +91,7 @@
     static final int NETWORK_SELECTION_NOTIFICATION = 2;
     static final int VOICEMAIL_NOTIFICATION = 3;
     static final int CALL_FORWARD_NOTIFICATION = 4;
-    static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5;
+    static final int DATA_ROAMING_NOTIFICATION = 5;
     static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
 
     // Event for network selection notification.
@@ -107,7 +108,6 @@
     private PhoneGlobals mApp;
 
     private Context mContext;
-    private NotificationManager mNotificationManager;
     private StatusBarManager mStatusBarManager;
     private UserManager mUserManager;
     private Toast mToast;
@@ -152,8 +152,6 @@
     private NotificationMgr(PhoneGlobals app) {
         mApp = app;
         mContext = app;
-        mNotificationManager =
-                (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
         mStatusBarManager =
                 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
         mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
@@ -378,7 +376,7 @@
                         && !user.isManagedProfile()) {
                     if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
                             pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
-                        mNotificationManager.notifyAsUser(
+                        notifyAsUser(
                                 Integer.toString(subId) /* tag */,
                                 VOICEMAIL_NOTIFICATION,
                                 notification,
@@ -396,7 +394,7 @@
                         && !user.isManagedProfile()) {
                     if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
                             false, userHandle, isRefresh)) {
-                        mNotificationManager.cancelAsUser(
+                        cancelAsUser(
                                 Integer.toString(subId) /* tag */,
                                 VOICEMAIL_NOTIFICATION,
                                 userHandle);
@@ -540,7 +538,7 @@
                     intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
             builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */,
                     intent, 0));
-            mNotificationManager.notifyAsUser(
+            notifyAsUser(
                     Integer.toString(subId) /* tag */,
                     CALL_FORWARD_NOTIFICATION,
                     builder.build(),
@@ -552,7 +550,7 @@
                     continue;
                 }
                 UserHandle userHandle = user.getUserHandle();
-                mNotificationManager.cancelAsUser(
+                cancelAsUser(
                         Integer.toString(subId) /* tag */,
                         CALL_FORWARD_NOTIFICATION,
                         userHandle);
@@ -561,39 +559,82 @@
     }
 
     /**
-     * Shows the "data disconnected due to roaming" notification, which
+     * Shows either:
+     * 1) the "Data roaming is on" notification, which
+     * appears when you're roaming and you have the "data roaming" feature turned on for the
+     * given {@code subId}.
+     * or
+     * 2) the "data disconnected due to roaming" notification, which
      * appears when you lose data connectivity because you're roaming and
      * you have the "data roaming" feature turned off for the given {@code subId}.
+     * @param subId which subscription it's notifying about.
+     * @param roamingOn whether currently roaming is on or off. If true, we show notification
+     *                  1) above; else we show notification 2).
      */
-    /* package */ void showDataDisconnectedRoaming(int subId) {
-        if (DBG) log("showDataDisconnectedRoaming()...");
+    /* package */ void showDataRoamingNotification(int subId, boolean roamingOn) {
+        if (DBG) {
+            log("showDataRoamingNotification() roaming " + (roamingOn ? "on" : "off")
+                    + " on subId " + subId);
+        }
 
         // "Mobile network settings" screen / dialog
         Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
         intent.putExtra(Settings.EXTRA_SUB_ID, subId);
         PendingIntent contentIntent = PendingIntent.getActivity(mContext, subId, intent, 0);
 
-        final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
+        CharSequence contentTitle = mContext.getText(roamingOn
+                ? R.string.roaming_on_notification_title
+                : R.string.roaming_notification_title);
+        CharSequence contentText = mContext.getText(roamingOn
+                ? R.string.roaming_enabled_message
+                : R.string.roaming_reenable_message);
 
         final Notification.Builder builder = new Notification.Builder(mContext)
                 .setSmallIcon(android.R.drawable.stat_sys_warning)
-                .setContentTitle(mContext.getText(R.string.roaming_notification_title))
+                .setContentTitle(contentTitle)
                 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
                 .setContentText(contentText)
                 .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
                 .setContentIntent(contentIntent);
         final Notification notif =
                 new Notification.BigTextStyle(builder).bigText(contentText).build();
-        mNotificationManager.notifyAsUser(
-                null /* tag */, DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif, UserHandle.ALL);
+        notifyAsUser(null /* tag */, DATA_ROAMING_NOTIFICATION, notif, UserHandle.ALL);
+    }
+
+    private void notifyAsUser(String tag, int id, Notification notification, UserHandle user) {
+        try {
+            Context contextForUser =
+                    mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
+            NotificationManager notificationManager =
+                    (NotificationManager) contextForUser.getSystemService(
+                            Context.NOTIFICATION_SERVICE);
+            notificationManager.notify(tag, id, notification);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(LOG_TAG, "unable to notify for user " + user);
+            e.printStackTrace();
+        }
+    }
+
+    private void cancelAsUser(String tag, int id, UserHandle user) {
+        try {
+            Context contextForUser =
+                    mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
+            NotificationManager notificationManager =
+                    (NotificationManager) contextForUser.getSystemService(
+                            Context.NOTIFICATION_SERVICE);
+            notificationManager.cancel(tag, id);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(LOG_TAG, "unable to cancel for user " + user);
+            e.printStackTrace();
+        }
     }
 
     /**
-     * Turns off the "data disconnected due to roaming" notification.
+     * Turns off the "data disconnected due to roaming" or "Data roaming is on" notification.
      */
-    /* package */ void hideDataDisconnectedRoaming() {
-        if (DBG) log("hideDataDisconnectedRoaming()...");
-        mNotificationManager.cancel(DATA_DISCONNECTED_ROAMING_NOTIFICATION);
+    /* package */ void hideDataRoamingNotification() {
+        if (DBG) log("hideDataRoamingNotification()...");
+        cancelAsUser(null, DATA_ROAMING_NOTIFICATION, UserHandle.ALL);
     }
 
     /**
@@ -626,7 +667,7 @@
                 mContext.getString(R.string.mobile_network_settings_class)));
         intent.putExtra(GsmUmtsOptions.EXTRA_SUB_ID, subId);
         builder.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0));
-        mNotificationManager.notifyAsUser(
+        notifyAsUser(
                 Integer.toString(subId) /* tag */,
                 SELECTED_OPERATOR_FAIL_NOTIFICATION,
                 builder.build(),
@@ -639,7 +680,7 @@
      */
     private void cancelNetworkSelection(int subId) {
         if (DBG) log("cancelNetworkSelection()...");
-        mNotificationManager.cancelAsUser(
+        cancelAsUser(
                 Integer.toString(subId) /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION,
                 UserHandle.ALL);
     }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 04850f3..250a295 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -16,6 +16,7 @@
 
 package com.android.phone;
 
+import android.annotation.IntDef;
 import android.app.Activity;
 import android.app.KeyguardManager;
 import android.app.ProgressDialog;
@@ -73,6 +74,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Global state for the telephony subsystem when running in the primary
@@ -107,11 +110,12 @@
     private static final int EVENT_SIM_NETWORK_LOCKED = 3;
     private static final int EVENT_SIM_STATE_CHANGED = 8;
     private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10;
-    private static final int EVENT_DATA_ROAMING_OK = 11;
-    private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 12;
-    private static final int EVENT_RESTART_SIP = 13;
-    private static final int EVENT_DATA_ROAMING_SETTINGS_CHANGED = 14;
-    private static final int EVENT_MOBILE_DATA_SETTINGS_CHANGED = 15;
+    private static final int EVENT_DATA_ROAMING_CONNECTED = 11;
+    private static final int EVENT_DATA_ROAMING_OK = 12;
+    private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 13;
+    private static final int EVENT_RESTART_SIP = 14;
+    private static final int EVENT_DATA_ROAMING_SETTINGS_CHANGED = 15;
+    private static final int EVENT_MOBILE_DATA_SETTINGS_CHANGED = 16;
 
     // The MMI codes are also used by the InCallScreen.
     public static final int MMI_INITIATE = 51;
@@ -158,7 +162,21 @@
     private Activity mPUKEntryActivity;
     private ProgressDialog mPUKEntryProgressDialog;
 
-    private boolean mNoDataDueToRoaming = false;
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ROAMING_NOTIFICATION_"},
+            value = {
+                    ROAMING_NOTIFICATION_NO_NOTIFICATION,
+                    ROAMING_NOTIFICATION_CONNECTED,
+                    ROAMING_NOTIFICATION_DISCONNECTED})
+    public @interface RoamingNotification {}
+
+    private static final int ROAMING_NOTIFICATION_NO_NOTIFICATION = 0;
+    private static final int ROAMING_NOTIFICATION_CONNECTED       = 1;
+    private static final int ROAMING_NOTIFICATION_DISCONNECTED    = 2;
+
+    @RoamingNotification
+    private int mPrevRoamingNotification = ROAMING_NOTIFICATION_NO_NOTIFICATION;
 
     private WakeState mWakeState = WakeState.SLEEP;
 
@@ -207,11 +225,15 @@
                     break;
 
                 case EVENT_DATA_ROAMING_DISCONNECTED:
-                    notificationMgr.showDataDisconnectedRoaming(msg.arg1);
+                    notificationMgr.showDataRoamingNotification(msg.arg1, false);
+                    break;
+
+                case EVENT_DATA_ROAMING_CONNECTED:
+                    notificationMgr.showDataRoamingNotification(msg.arg1, true);
                     break;
 
                 case EVENT_DATA_ROAMING_OK:
-                    notificationMgr.hideDataDisconnectedRoaming();
+                    notificationMgr.hideDataRoamingNotification();
                     break;
 
                 case MMI_COMPLETE:
@@ -721,6 +743,16 @@
     }
 
     /**
+     * @return whether or not we should show a notification when connecting to data roaming if the
+     * user has data roaming enabled
+     */
+    private boolean shouldShowDataConnectedRoaming(int subId) {
+        PersistableBundle config = getCarrierConfigForSubId(subId);
+        return config.getBoolean(CarrierConfigManager
+                .KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL);
+    }
+
+    /**
      * When roaming, if mobile data cannot be established due to data roaming not enabled, we need
      * to notify the user so they can enable it through settings. Vise versa if the condition
      * changes, we need to dismiss the notification.
@@ -737,27 +769,48 @@
         boolean dataAllowed = phone.isDataAllowed(ApnSetting.TYPE_DEFAULT, reasons);
         mDataRoamingNotifLog.log("dataAllowed=" + dataAllowed + ", reasons=" + reasons);
         if (VDBG) Log.v(LOG_TAG, "dataAllowed=" + dataAllowed + ", reasons=" + reasons);
-        if (!mNoDataDueToRoaming
-                && !dataAllowed
-                && reasons.containsOnly(DataDisallowedReasonType.ROAMING_DISABLED)) {
+        if (!dataAllowed && reasons.containsOnly(DataDisallowedReasonType.ROAMING_DISABLED)) {
+            // No need to show it again if we never cancelled it explicitly.
+            if (mPrevRoamingNotification == ROAMING_NOTIFICATION_DISCONNECTED) return;
             // If the only reason of no data is data roaming disabled, then we notify the user
             // so the user can turn on data roaming.
-            mNoDataDueToRoaming = true;
+            mPrevRoamingNotification = ROAMING_NOTIFICATION_DISCONNECTED;
             Log.d(LOG_TAG, "Show roaming disconnected notification");
-            mDataRoamingNotifLog.log("Show");
+            mDataRoamingNotifLog.log("Show roaming off.");
             Message msg = mHandler.obtainMessage(EVENT_DATA_ROAMING_DISCONNECTED);
             msg.arg1 = mDefaultDataSubId;
             msg.sendToTarget();
-        } else if (mNoDataDueToRoaming && (dataAllowed
-                || !reasons.containsOnly(DataDisallowedReasonType.ROAMING_DISABLED))) {
-            // Otherwise dismiss the notification we showed earlier.
-            mNoDataDueToRoaming = false;
-            Log.d(LOG_TAG, "Dismiss roaming disconnected notification");
+        } else if (dataAllowed && dataIsNowRoaming(mDefaultDataSubId)
+                && shouldShowDataConnectedRoaming(mDefaultDataSubId)) {
+            // No need to show it again if we never cancelled it explicitly, or carrier config
+            // indicates this is not needed.
+            if (mPrevRoamingNotification == ROAMING_NOTIFICATION_CONNECTED) return;
+            mPrevRoamingNotification = ROAMING_NOTIFICATION_CONNECTED;
+            Log.d(LOG_TAG, "Show roaming connected notification");
+            mDataRoamingNotifLog.log("Show roaming on.");
+            Message msg = mHandler.obtainMessage(EVENT_DATA_ROAMING_CONNECTED);
+            msg.arg1 = mDefaultDataSubId;
+            msg.sendToTarget();
+        } else if (mPrevRoamingNotification != ROAMING_NOTIFICATION_NO_NOTIFICATION) {
+            // Otherwise we either 1) we are not roaming or 2) roaming is off but ROAMING_DISABLED
+            // is not the only data disable reason. In this case we dismiss the notification we
+            // showed earlier.
+            mPrevRoamingNotification = ROAMING_NOTIFICATION_NO_NOTIFICATION;
+            Log.d(LOG_TAG, "Dismiss roaming notification");
             mDataRoamingNotifLog.log("Hide. data allowed=" + dataAllowed + ", reasons=" + reasons);
             mHandler.sendEmptyMessage(EVENT_DATA_ROAMING_OK);
         }
     }
 
+    /**
+     *
+     * @param subId to check roaming on
+     * @return whether we have transitioned to dataRoaming
+     */
+    private boolean dataIsNowRoaming(int subId) {
+        return getPhone(subId).getServiceState().getDataRoaming();
+    }
+
     public Phone getPhoneInEcm() {
         return phoneInEcm;
     }
@@ -796,7 +849,7 @@
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
         pw.println("------- PhoneGlobals -------");
         pw.increaseIndent();
-        pw.println("mNoDataDueToRoaming=" + mNoDataDueToRoaming);
+        pw.println("mPrevRoamingNotification=" + mPrevRoamingNotification);
         pw.println("mDefaultDataSubId=" + mDefaultDataSubId);
         pw.println("mDataRoamingNotifLog:");
         pw.increaseIndent();