Merge "Add an intent for the advanced connected devices."
diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml
index 779e504..f81c7e8 100644
--- a/res/layout/master_clear.xml
+++ b/res/layout/master_clear.xml
@@ -93,7 +93,7 @@
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_gravity="center_vertical"
-                        android:paddingEnd="8dp"
+                        android:paddingEnd="@dimen/reset_checkbox_padding_end"
                         android:focusable="false"
                         android:clickable="false"
                         android:duplicateParentState="true" />
@@ -104,14 +104,14 @@
                     <TextView
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:paddingTop="12dp"
-                        android:textSize="18sp"
+                        android:paddingTop="@dimen/reset_checkbox_title_padding_top"
+                        android:textSize="@dimen/reset_checkbox_title_text_size"
                         android:text="@string/erase_external_storage" />
                     <TextView
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:paddingTop="4sp"
-                        android:textSize="14sp"
+                        android:paddingTop="@dimen/reset_checkbox_summary_padding_top"
+                        android:textSize="@dimen/reset_checkbox_summary_text_size"
                         android:text="@string/erase_external_storage_description" />
                 </LinearLayout>
             </LinearLayout>
diff --git a/res/layout/reset_esim_checkbox.xml b/res/layout/reset_esim_checkbox.xml
new file mode 100644
index 0000000..e76ced0
--- /dev/null
+++ b/res/layout/reset_esim_checkbox.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:focusable="true"
+    android:clickable="true"
+    android:visibility="gone">
+
+    <CheckBox android:id="@+id/erase_esim"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingEnd="@dimen/reset_checkbox_padding_end"
+        android:focusable="false"
+        android:clickable="false"
+        android:duplicateParentState="true" />
+
+    <LinearLayout android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical">
+
+        <TextView android:id="@+id/erase_esim_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="@dimen/reset_checkbox_title_padding_top"
+            android:textSize="@dimen/reset_checkbox_title_text_size" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="@dimen/reset_checkbox_summary_padding_top"
+            android:textSize="@dimen/reset_checkbox_summary_text_size"
+            android:text="@string/reset_esim_desc" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/reset_network.xml b/res/layout/reset_network.xml
index be966dd..1850bb2 100644
--- a/res/layout/reset_network.xml
+++ b/res/layout/reset_network.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical" >
@@ -27,7 +28,8 @@
         android:layout_marginTop="12dp"
         android:layout_weight="1">
 
-        <LinearLayout android:layout_width="match_parent"
+        <LinearLayout
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical">
 
@@ -38,6 +40,11 @@
                 android:textDirection="locale"
                 android:text="@string/reset_network_desc" />
 
+            <include layout="@layout/reset_esim_checkbox"
+                android:id="@+id/erase_esim_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
         </LinearLayout>
 
     </ScrollView>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 339eaf2..d4071ed 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -311,4 +311,11 @@
     <dimen name="suggestion_card_title_padding_bottom_one_card">6dp</dimen>
     <dimen name="suggestion_card_title_padding_bottom_multiple_cards">8dp</dimen>
 
+    <!-- Padding for the reset screens -->
+    <dimen name="reset_checkbox_padding_end">8dp</dimen>
+    <dimen name="reset_checkbox_title_padding_top">12dp</dimen>
+    <dimen name="reset_checkbox_summary_padding_top">4dp</dimen>
+    <dimen name="reset_checkbox_title_text_size">18sp</dimen>
+    <dimen name="reset_checkbox_summary_text_size">14sp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4182fd9..164f6d5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3213,6 +3213,10 @@
     <string name="reset_network_title">Reset Wi-Fi, mobile &amp; Bluetooth</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Reset network settings [CHAR LIMIT=NONE] -->
     <string name="reset_network_desc">This will reset all network settings, including:\n\n<li>Wi\u2011Fi</li>\n<li>Mobile data</li>\n<li>Bluetooth</li>"</string>
+    <!-- SD card & phone storage settings screen, title for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=NONE] -->
+    <string name="reset_esim_title">Also reset eSIMs</string>
+    <!-- SD card & phone storage settings screen, message for the checkbox to let user decide whether erase eSIM data together [CHAR LIMIT=NONE] -->
+    <string name="reset_esim_desc">Erase all eSIMs on the phone. You\u2019ll have to contract your carrier to redownload your eSIMs. This will not cancel your mobile service plan.</string>
     <!-- SD card & phone storage settings screen, button on screen after user selects Reset network settings -->
     <string name="reset_network_button_text">Reset settings</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Reset settings button -->
@@ -3225,6 +3229,10 @@
     <string name="network_reset_not_available">Network reset is not available for this user</string>
     <!-- Reset settings complete toast text [CHAR LIMIT=75] -->
     <string name="reset_network_complete_toast">Network settings have been reset</string>
+    <!-- Title of the error message shown when error happens during erase eSIM data [CHAR LIMIT=NONE] -->
+    <string name="reset_esim_error_title">Cant\u2019t reset eSIMs</string>
+    <!-- Message of the error message shown when error happens during erase eSIM data [CHAR LIMIT=NONE] -->
+    <string name="reset_esim_error_msg">The eSIMs can\u2019tt be reset due to an error.</string>
 
     <!-- Master Clear -->
     <!-- Button title to factory data reset the entire device -->
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index 0f08c26..f64f6dc 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -18,20 +18,28 @@
 
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.CheckBox;
 import android.widget.Spinner;
+import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.PhoneConstants;
@@ -64,6 +72,8 @@
     private View mContentView;
     private Spinner mSubscriptionSpinner;
     private Button mInitiateButton;
+    private View mEsimContainer;
+    private CheckBox mEsimCheckbox;
 
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -107,6 +117,7 @@
             SubscriptionInfo subscription = mSubscriptions.get(selectedIndex);
             args.putInt(PhoneConstants.SUBSCRIPTION_KEY, subscription.getSubscriptionId());
         }
+        args.putBoolean(MasterClear.ERASE_ESIMS_EXTRA, mEsimCheckbox.isChecked());
         ((SettingsActivity) getActivity()).startPreferencePanel(
                 this, ResetNetworkConfirm.class.getName(),
                 args, R.string.reset_network_confirm_title, null, null, 0);
@@ -141,6 +152,8 @@
      */
     private void establishInitialState() {
         mSubscriptionSpinner = (Spinner) mContentView.findViewById(R.id.reset_network_subscription);
+        mEsimContainer = mContentView.findViewById(R.id.erase_esim_container);
+        mEsimCheckbox = mContentView.findViewById(R.id.erase_esim);
 
         mSubscriptions = SubscriptionManager.from(getActivity()).getActiveSubscriptionInfoList();
         if (mSubscriptions != null && mSubscriptions.size() > 0) {
@@ -192,6 +205,30 @@
         }
         mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_reset_network);
         mInitiateButton.setOnClickListener(mInitiateListener);
+        if (showEuiccSettings(getContext())) {
+            mEsimContainer.setVisibility(View.VISIBLE);
+            TextView title = mContentView.findViewById(R.id.erase_esim_title);
+            title.setText(R.string.reset_esim_title);
+            mEsimContainer.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mEsimCheckbox.toggle();
+                }
+            });
+        } else {
+            mEsimCheckbox.setChecked(false /* checked */);
+        }
+    }
+
+    private boolean showEuiccSettings(Context context) {
+        EuiccManager euiccManager =
+                (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
+        if (!euiccManager.isEnabled()) {
+            return false;
+        }
+        ContentResolver resolver = context.getContentResolver();
+        return Settings.Global.getInt(resolver, Global.EUICC_PROVISIONED, 0) != 0
+                || Settings.Global.getInt(resolver, Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
     }
 
     @Override
diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java
index 58b8289..53d9386 100644
--- a/src/com/android/settings/ResetNetworkConfirm.java
+++ b/src/com/android/settings/ResetNetworkConfirm.java
@@ -16,6 +16,7 @@
 
 package com.android.settings;
 
+import android.app.AlertDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
 import android.content.ContentResolver;
@@ -24,9 +25,12 @@
 import android.net.NetworkPolicyManager;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.RecoverySystem;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.view.LayoutInflater;
@@ -39,6 +43,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.wrapper.RecoverySystemWrapper;
 import com.android.settingslib.RestrictedLockUtils;
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -57,6 +62,43 @@
 
     private View mContentView;
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    @VisibleForTesting boolean mEraseEsim;
+    @VisibleForTesting EraseEsimAsyncTask mEraseEsimTask;
+    @VisibleForTesting static RecoverySystemWrapper mRecoverySystem;
+
+    /**
+     * Async task used to erase all the eSIM profiles from the phone. If error happens during
+     * erasing eSIM profiles or timeout, an error msg is shown.
+     */
+    private static class EraseEsimAsyncTask extends AsyncTask<Void, Void, Boolean> {
+        private final Context mContext;
+        private final String mPackageName;
+
+        EraseEsimAsyncTask(Context context, String packageName) {
+            mContext = context;
+            mPackageName = packageName;
+        }
+
+        @Override
+        protected Boolean doInBackground(Void... params) {
+            return mRecoverySystem.wipeEuiccData(
+                    mContext, true /* isWipeEuicc */, mPackageName);
+        }
+
+        @Override
+        protected void onPostExecute(Boolean succeeded) {
+            if (succeeded) {
+                Toast.makeText(mContext, R.string.reset_network_complete_toast, Toast.LENGTH_SHORT)
+                        .show();
+            } else {
+                new AlertDialog.Builder(mContext)
+                        .setTitle(R.string.reset_esim_error_title)
+                        .setMessage(R.string.reset_esim_error_msg)
+                        .setPositiveButton(android.R.string.ok, null /* listener */)
+                        .show();
+            }
+        }
+    }
 
     /**
      * The user has gone through the multiple confirmation, so now we go ahead
@@ -69,7 +111,8 @@
             if (Utils.isMonkeyRunning()) {
                 return;
             }
-            // TODO maybe show a progress dialog if this ends up taking a while
+            // TODO maybe show a progress screen if this ends up taking a while and won't let user
+            // go back until the tasks finished.
             Context context = getActivity();
 
             ConnectivityManager connectivityManager = (ConnectivityManager)
@@ -108,11 +151,20 @@
 
             ImsManager.factoryReset(context);
             restoreDefaultApn(context);
+            esimFactoryReset(context, context.getPackageName());
+        }
+    };
 
+    @VisibleForTesting
+    void esimFactoryReset(Context context, String packageName) {
+        if (mEraseEsim) {
+            mEraseEsimTask = new EraseEsimAsyncTask(context, packageName);
+            mEraseEsimTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        } else {
             Toast.makeText(context, R.string.reset_network_complete_toast, Toast.LENGTH_SHORT)
                     .show();
         }
-    };
+    }
 
     /**
      * Restore APN settings to default.
@@ -163,6 +215,16 @@
         if (args != null) {
             mSubId = args.getInt(PhoneConstants.SUBSCRIPTION_KEY,
                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            mEraseEsim = args.getBoolean(MasterClear.ERASE_ESIMS_EXTRA);
+        }
+        mRecoverySystem = new RecoverySystemWrapper();
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mEraseEsimTask != null) {
+            mEraseEsimTask.cancel(true /* mayInterruptIfRunning */);
+            mEraseEsimTask = null;
         }
     }
 
diff --git a/src/com/android/settings/wrapper/RecoverySystemWrapper.java b/src/com/android/settings/wrapper/RecoverySystemWrapper.java
new file mode 100644
index 0000000..8a36969
--- /dev/null
+++ b/src/com/android/settings/wrapper/RecoverySystemWrapper.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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.settings.wrapper;
+
+import android.content.Context;
+import android.os.RecoverySystem;
+
+/**
+ * This class replicates a subset of the {@link RecoverySystem}.
+ * The interface exists so that we can use a thin wrapper around the RecoverySystem in
+ * production code and a mock in tests.
+ */
+public class RecoverySystemWrapper {
+
+    /**
+     * Returns whether wipe Euicc data successfully or not.
+     *
+     * @param isWipeEuicc whether we want to wipe Euicc data or not
+     * @param packageName the package name of the caller app.
+     */
+    public boolean wipeEuiccData(
+            Context context, final boolean isWipeEuicc, final String packageName) {
+        return RecoverySystem.wipeEuiccData(context, isWipeEuicc, packageName);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java b/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
new file mode 100644
index 0000000..354cacf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 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.settings;
+
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.content.Context;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.RecoverySystemWrapper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(
+    manifest = TestConfig.MANIFEST_PATH,
+    sdk = TestConfig.SDK_VERSION
+)
+public class ResetNetworkConfirmTest {
+
+    private Activity mActivity;
+    @Mock
+    private ResetNetworkConfirm mResetNetworkConfirm;
+    @Mock
+    private RecoverySystemWrapper mRecoverySystem;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mResetNetworkConfirm = spy(new ResetNetworkConfirm());
+        mRecoverySystem = spy(new RecoverySystemWrapper());
+        ResetNetworkConfirm.mRecoverySystem = mRecoverySystem;
+        mActivity = Robolectric.setupActivity(Activity.class);
+    }
+
+    @Test
+    public void testResetNetworkData_resetEsim() {
+        mResetNetworkConfirm.mEraseEsim = true;
+        doReturn(true)
+                .when(mRecoverySystem).wipeEuiccData(any(Context.class), anyBoolean(), anyString());
+
+        mResetNetworkConfirm.esimFactoryReset(mActivity, "" /* packageName */);
+        try {
+            // Waiting the Async task finished
+            Thread.sleep(10000); // 10 sec
+        } catch (InterruptedException ignore) {
+
+        }
+
+        Assert.assertNotNull(mResetNetworkConfirm.mEraseEsimTask);
+        verify(mRecoverySystem).wipeEuiccData(any(Context.class), anyBoolean(), anyString());
+    }
+
+    @Test
+    public void testResetNetworkData_notResetEsim() {
+        mResetNetworkConfirm.mEraseEsim = false;
+
+        mResetNetworkConfirm.esimFactoryReset(mActivity, "" /* packageName */);
+
+        Assert.assertNull(mResetNetworkConfirm.mEraseEsimTask);
+        verify(mRecoverySystem, never())
+                .wipeEuiccData(any(Context.class), anyBoolean(), anyString());
+    }
+}