Merge the android-9.0.0_r60 release tag
Android 9.0.0 release 60

* tag 'android-9.0.0_r60':
  Restrict access to the resolution PendingIntent created by EuiccController to EuiccResolutionUiDispatcherActivity
  Specify the component for the ACTION_SHOW_ECM_EXIT_DIALOG Intent

Change-Id: Ibfd83b9773426d5a388b75323abea8c9ebcce544
diff --git a/Android.mk b/Android.mk
index e30a4fc..1070732 100644
--- a/Android.mk
+++ b/Android.mk
@@ -19,7 +19,8 @@
         android-support-v7-appcompat \
         android-support-v7-preference \
         android-support-v7-recyclerview \
-        android-support-v14-preference
+        android-support-v14-preference \
+        android-support-v4
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
         guava \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 13fd30a..33cd319 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -607,5 +607,6 @@
                 <action android:name="android.telephony.data.DataService" />
             </intent-filter>
         </service>
+        <service android:name=".SpecialCarrierAppIntentService" />
     </application>
 </manifest>
diff --git a/res/drawable/ic_special_carrier_app_disable.xml b/res/drawable/ic_special_carrier_app_disable.xml
new file mode 100644
index 0000000..480c19a
--- /dev/null
+++ b/res/drawable/ic_special_carrier_app_disable.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z"/>
+</vector>
diff --git a/res/drawable/ic_special_carrier_app_enable.xml b/res/drawable/ic_special_carrier_app_enable.xml
new file mode 100644
index 0000000..edaf734
--- /dev/null
+++ b/res/drawable/ic_special_carrier_app_enable.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
+</vector>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 20a5cbb..201e7be 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -747,4 +747,10 @@
     <string name="supp_service_forwarded_call" msgid="4509980341645679803">"Appel transféré."</string>
     <string name="supp_service_conference_call" msgid="4448616364004466832">"Connexion en cours à la conférence téléphonique."</string>
     <string name="supp_service_held_call_released" msgid="2030677825038709779">"L\'appel en attente a été interrompu."</string>
+    <string name="special_carrier_app_disable">"Désactiver"</string>
+    <string name="special_carrier_app_enable">"Activer"</string>
+    <string name="special_carrier_app_generic_carrier_name">"Votre opérateur"</string>
+    <string name="special_carrier_app_for_uicc">"Application opérateur"</string>
+    <string name="special_carrier_app_summary">"%1$s vous propose une application."</string>
+    <string name="special_carrier_app_summary_extended">"%1$s vous propose une application. Voulez-vous l'activer ?"</string>
 </resources>
diff --git a/res/values-mcc208-mnc01-fr/strings.xml b/res/values-mcc208-mnc01-fr/strings.xml
new file mode 100644
index 0000000..f016d42
--- /dev/null
+++ b/res/values-mcc208-mnc01-fr/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="special_carrier_app_summary">"Orange vous propose l’application Apps Orange."</string>
+    <string name="special_carrier_app_summary_extended">"Orange vous propose l’application Apps Orange. Apps Orange offre un accès rapide à un catalogue d’applications Orange et partenaires. Voulez-vous l’activer ?"</string>
+</resources>
diff --git a/res/values-mcc208-mnc02-fr b/res/values-mcc208-mnc02-fr
new file mode 120000
index 0000000..45a205d
--- /dev/null
+++ b/res/values-mcc208-mnc02-fr
@@ -0,0 +1 @@
+values-mcc208-mnc01-fr
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f728109..ae269ec 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1748,4 +1748,23 @@
     <!-- Message displayed to the user to indicate that a held call has been released /
          disconnected. -->
     <string name="supp_service_held_call_released">Held call has been released.</string>
+
+    <!-- Special Carrier App notification -->
+    <!-- [CHAR LIMIT=25] Button to disable a special carrier app. -->
+    <string name="special_carrier_app_disable">Disable</string>
+    <!-- [CHAR LIMIT=25] Button to enable special carrier app. -->
+    <string name="special_carrier_app_enable">Enable</string>
+    <!-- Generic carrier name in case the carrier name is not available. -->
+    <string name="special_carrier_app_generic_carrier_name">Your operator</string>
+    <!-- [CHAR_LIMIT=24] -->
+    <string name="special_carrier_app_for_uicc">Special operator app</string>
+    <!-- [CHAR_LIMIT=40] Arguments: <Operator name> -->
+    <string name="special_carrier_app_summary"><xliff:g id="operator" example="Your operator">%1$s</xliff:g> offers a special app.</string>
+    <!-- Arguments: <Operator name> -->
+    <string name="special_carrier_app_summary_extended"><xliff:g id="operator" example="Your operator">%1$s</xliff:g> offers a special app. Do you want to enable it?</string>
+
+    <!-- Notification channel configuration -->
+    <string name="notification_channel_id">notification_channel_01</string>
+    <string name="notification_channel_name">Notifications</string>
+    <string name="notification_channel_description">Notifications</string>
 </resources>
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 691d9ff..0f9ec9e 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -318,6 +318,7 @@
         private MyHandler mHandler;
         private boolean mOkClicked;
         private boolean mExpandAdvancedFields;
+        private boolean canPreferenceChange;
 
         // We assume the the value returned by mTabHost.getCurrentTab() == slotId
         private TabHost mTabHost;
@@ -933,6 +934,11 @@
             final PreferenceScreen prefSet = getPreferenceScreen();
             final int phoneSubId = mPhone.getSubId();
             final boolean hasActiveSubscriptions = hasActiveSubscriptions();
+            Context context = activity.getApplicationContext();
+            int network_mode = android.provider.Settings.Global.getInt(
+                        context.getContentResolver(),
+                        android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
+                        preferredNetworkMode);
 
             if (activity == null || activity.isDestroyed()) {
                 Log.e(LOG_TAG, "updateBody with no valid activity.");
@@ -946,6 +952,23 @@
 
             prefSet.removeAll();
 
+          if(phoneSubId != SubscriptionManager.getDefaultDataSubscriptionId()) {
+              if(network_mode == Phone.NT_MODE_LTE_GSM_WCDMA) {
+                        android.provider.Settings.Global.putInt(
+                            context.getContentResolver(),
+                            android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
+                            Phone.NT_MODE_GSM_ONLY);
+              }
+          }
+          else if(phoneSubId == SubscriptionManager.getDefaultDataSubscriptionId() && !canPreferenceChange){
+                    if(network_mode == Phone.NT_MODE_GSM_ONLY) {
+                        android.provider.Settings.Global.putInt(
+                            context.getContentResolver(),
+                            android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
+                            Phone.NT_MODE_LTE_GSM_WCDMA);
+                    }
+            canPreferenceChange = false;
+            }
             updateBodyBasicFields(activity, prefSet, phoneSubId, hasActiveSubscriptions);
 
             if (mExpandAdvancedFields) {
@@ -1152,6 +1175,11 @@
                         .setOnPreferenceChangeListener(this);
             }
 
+            boolean canChangeNetworkMode = true;
+            if ((settingsNetworkMode == Phone.NT_MODE_GSM_ONLY) && (phoneSubId != SubscriptionManager.getDefaultDataSubscriptionId())) {
+              canChangeNetworkMode = false;
+            }
+
             // Get the networkMode from Settings.System and displays it
             mButtonEnabledNetworks.setValue(Integer.toString(settingsNetworkMode));
             mButtonPreferredNetworkMode.setValue(Integer.toString(settingsNetworkMode));
@@ -1177,7 +1205,7 @@
                     R.string.enhanced_4g_lte_mode_title;
 
             mButtonPreferredNetworkMode.setEnabled(hasActiveSubscriptions);
-            mButtonEnabledNetworks.setEnabled(hasActiveSubscriptions);
+            mButtonEnabledNetworks.setEnabled(hasActiveSubscriptions && canChangeNetworkMode);
             mButton4glte.setTitle(enhanced4glteModeTitleId);
             mLteDataServicePref.setEnabled(hasActiveSubscriptions);
             Preference ps;
@@ -1289,6 +1317,7 @@
                             .obtainMessage(MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE));
                 }
             } else if (preference == mButtonEnabledNetworks) {
+                canPreferenceChange = true;
                 mButtonEnabledNetworks.setValue((String) objValue);
                 int buttonNetworkMode;
                 buttonNetworkMode = Integer.parseInt((String) objValue);
diff --git a/src/com/android/phone/SpecialCarrierAppIntentService.java b/src/com/android/phone/SpecialCarrierAppIntentService.java
new file mode 100644
index 0000000..440a3ad
--- /dev/null
+++ b/src/com/android/phone/SpecialCarrierAppIntentService.java
@@ -0,0 +1,211 @@
+package com.android.phone;
+
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.support.v4.app.NotificationCompat;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.telephony.SpecialCarrierAppInfo;
+
+public class SpecialCarrierAppIntentService extends IntentService {
+
+    private static final String TAG = "SpecialCarrierApp";
+
+    private static final boolean DEBUG = false; // STOPSHIP if true
+
+    /**
+     * Action to handle a match.
+     * The app will be enabled if it matched while the device was not provisioned, otherwise
+     * a notification will be displayed to let the user enable the app (or not).
+     */
+    public static final String ACTION_HANDLE_MATCH = "com.android.phone.SpecialCarrierApp.handle_match";
+    /**
+     * Action to dismiss the offer to enable/disable the carrier app (will be offered again).
+     */
+    public static final String ACTION_DISMISS_OFFER = "com.android.phone.SpecialCarrierApp.dismiss_notification";
+    /**
+     * Action to enable the app.
+     */
+    public static final String ACTION_ENABLE_APP = "com.android.phone.SpecialCarrierApp.enable_app";
+    /**
+     * Action to disable the app.
+     */
+    public static final String ACTION_DISABLE_APP = "com.android.phone.SpecialCarrierApp.disable_app";
+
+    public static final String EXTRA_APP_INFO = "com.android.phone.SpecialCarrierApp.app_info";
+    public static final String EXTRA_DEVICE_PROVISIONED = "com.android.phone.SpecialCarrierApp.device_provisioned";
+
+    private IPackageManager mPackageManager;
+
+    public SpecialCarrierAppIntentService() {
+        super("SpecialCarrierAppIntentService");
+
+        mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+    }
+
+    public static PendingIntent getPendingDismissIntent(Context context, SpecialCarrierAppInfo appInfo) {
+        final Intent intent = new Intent(context, SpecialCarrierAppIntentService.class)
+                .setAction(ACTION_DISMISS_OFFER)
+                .putExtra(EXTRA_APP_INFO, appInfo);
+        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+    }
+
+    public static PendingIntent getPendingEnableIntent(Context context, SpecialCarrierAppInfo appInfo) {
+        final Intent intent = new Intent(context, SpecialCarrierAppIntentService.class)
+                .setAction(ACTION_ENABLE_APP)
+                .putExtra(EXTRA_APP_INFO, appInfo);
+        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+    }
+
+    public static PendingIntent getPendingDisableIntent(Context context, SpecialCarrierAppInfo appInfo) {
+        final Intent intent = new Intent(context, SpecialCarrierAppIntentService.class)
+                .setAction(ACTION_DISABLE_APP)
+                .putExtra(EXTRA_APP_INFO, appInfo);
+        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (intent != null) {
+            final String action = intent.getAction();
+            if (DEBUG) Slog.d(TAG, "Received intent with action: " + action);
+
+            final Object appInfoObject = intent.getSerializableExtra(EXTRA_APP_INFO);
+            if (appInfoObject == null || !(appInfoObject instanceof SpecialCarrierAppInfo)) {
+                Slog.wtf(TAG, "EXTRA_APP_INFO is mandatory");
+                return;
+            }
+            final SpecialCarrierAppInfo appInfo = (SpecialCarrierAppInfo) appInfoObject;
+
+            if (ACTION_DISMISS_OFFER.equals(action)) {
+                handleActionDismissOffer(appInfo);
+            } else if (ACTION_ENABLE_APP.equals(action)) {
+                handleActionEnableApp(appInfo);
+            } else if (ACTION_DISABLE_APP.equals(action)) {
+                handleActionDisableApp(appInfo);
+            } else if (ACTION_HANDLE_MATCH.equals(action)) {
+                handleActionHandleMatch(appInfo,
+                        intent.getBooleanExtra(EXTRA_DEVICE_PROVISIONED, true));
+            } else {
+                Slog.w(TAG, "Unknown action: " + action);
+            }
+        }
+    }
+
+    private void handleActionDismissOffer(SpecialCarrierAppInfo appInfo) {
+        Slog.d(TAG, "Dismissed, the special carrier app " + appInfo.mPackageName + " will be offered again.");
+    }
+
+    private void handleActionHandleMatch(SpecialCarrierAppInfo appInfo, boolean wasDeviceProvisioned) {
+        if (!wasDeviceProvisioned) {
+            if (DEBUG) Slog.d(TAG, "The device was not yet provisioned when the special carrier app matched.");
+
+            enableSpecialCarrierApp(appInfo);
+        } else {
+            if (DEBUG) Slog.d(TAG, "The device was already provisioned when the special carrier app matched.");
+
+            showNotification(appInfo);
+        }
+    }
+
+    private void handleActionEnableApp(SpecialCarrierAppInfo appInfo) {
+        enableSpecialCarrierApp(appInfo);
+        cancelNotification(appInfo);
+    }
+
+    private void handleActionDisableApp(SpecialCarrierAppInfo appInfo) {
+        disableSpecialCarrierApp(appInfo);
+        cancelNotification(appInfo);
+    }
+
+    private void showNotification(SpecialCarrierAppInfo appInfo) {
+        createNotificationChannel(getApplicationContext());
+        final int slotNumberToDisplay = appInfo.mUiccSlotId + 1;
+
+        if (TextUtils.isEmpty(appInfo.mCarrierName)) {
+            appInfo.mCarrierName = getString(R.string.special_carrier_app_generic_carrier_name);
+        }
+
+        final String channelId = getString(R.string.notification_channel_id);
+        final Notification notification =
+                new NotificationCompat.Builder(getApplicationContext(), channelId)
+                .setDeleteIntent(getPendingDismissIntent(getApplicationContext(), appInfo))
+                .setContentTitle(getString(R.string.special_carrier_app_for_uicc))
+                .setContentText(getString(R.string.special_carrier_app_summary, appInfo.mCarrierName))
+                .setSmallIcon(com.android.internal.R.drawable.ic_sim_card_multi_48px_clr)
+                .setStyle(new NotificationCompat.BigTextStyle().bigText(
+                        getString(R.string.special_carrier_app_summary_extended, appInfo.mCarrierName, slotNumberToDisplay)))
+                .addAction(
+                        new NotificationCompat.Action.Builder(
+                                R.drawable.ic_special_carrier_app_disable,
+                                getString(R.string.special_carrier_app_disable),
+                                getPendingDisableIntent(getApplicationContext(), appInfo)).build())
+                .addAction(
+                        new NotificationCompat.Action.Builder(
+                                R.drawable.ic_special_carrier_app_enable,
+                                getString(R.string.special_carrier_app_enable),
+                                getPendingEnableIntent(getApplicationContext(), appInfo)).build())
+                .build();
+
+        ((NotificationManager) getApplicationContext()
+                .getSystemService(Context.NOTIFICATION_SERVICE)
+        ).notify(appInfo.mUiccSlotId, notification);
+    }
+
+    private void createNotificationChannel(Context context) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            String channelId = context.getString(R.string.notification_channel_id);
+            CharSequence name = context.getString(R.string.notification_channel_name);
+            String description = context.getString(R.string.notification_channel_description);
+            int importance = NotificationManager.IMPORTANCE_DEFAULT;
+
+            NotificationChannel channel = new NotificationChannel(channelId, name, importance);
+            channel.setDescription(description);
+            ((NotificationManager) getApplicationContext()
+                    .getSystemService(Context.NOTIFICATION_SERVICE)
+            ).createNotificationChannel(channel);
+        }
+    }
+
+    private void cancelNotification(SpecialCarrierAppInfo appInfo) {
+        ((NotificationManager) getApplicationContext()
+                .getSystemService(Context.NOTIFICATION_SERVICE)
+        ).cancel(appInfo.mUiccSlotId);
+    }
+
+    private void enableSpecialCarrierApp(SpecialCarrierAppInfo appInfo) {
+        updateCarrierAppState(appInfo, true);
+        if (appInfo.mActionIntentToBroadcast != null) {
+            getApplicationContext().sendBroadcast(new Intent(appInfo.mActionIntentToBroadcast));
+        }
+    }
+
+    private void disableSpecialCarrierApp(SpecialCarrierAppInfo appInfo) {
+        updateCarrierAppState(appInfo, false);
+    }
+
+    private void updateCarrierAppState(SpecialCarrierAppInfo appInfo, boolean enable) {
+        Slog.i(TAG, "Update state(" + appInfo.mPackageName + "): " + (enable ? "ENABLED" : "DISABLED")
+                + " for user " + getApplicationContext().getUserId());
+        try {
+            mPackageManager.setApplicationEnabledSetting(appInfo.mPackageName,
+                    enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                            : PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER,
+                    enable ? PackageManager.DONT_KILL_APP : 0, getApplicationContext().getUserId(),
+                    getApplicationContext().getOpPackageName());
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not reach PackageManager", e);
+        }
+    }
+}