diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml
index 779e50462c9..f81c7e81c10 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 @@
diff --git a/res/layout/reset_esim_checkbox.xml b/res/layout/reset_esim_checkbox.xml
new file mode 100644
index 00000000000..e76ced054f2
--- /dev/null
+++ b/res/layout/reset_esim_checkbox.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/reset_network.xml b/res/layout/reset_network.xml
index be966ddce4a..1850bb23be6 100644
--- a/res/layout/reset_network.xml
+++ b/res/layout/reset_network.xml
@@ -14,7 +14,8 @@
limitations under the License.
-->
-
@@ -27,7 +28,8 @@
android:layout_marginTop="12dp"
android:layout_weight="1">
-
@@ -38,6 +40,11 @@
android:textDirection="locale"
android:text="@string/reset_network_desc" />
+
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 339eaf2bf56..d4071ed359a 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -311,4 +311,11 @@
6dp
8dp
+
+ 8dp
+ 12dp
+ 4dp
+ 18sp
+ 14sp
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 317a2f55c86..2af4476c27c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3213,6 +3213,10 @@
Reset Wi-Fi, mobile & Bluetooth
This will reset all network settings, including:\n\nWi\u2011Fi\nMobile data\nBluetooth"
+
+ Also reset eSIMs
+
+ 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.
Reset settings
@@ -3225,6 +3229,10 @@
Network reset is not available for this user
Network settings have been reset
+
+ Cant\u2019t reset eSIMs
+
+ The eSIMs can\u2019tt be reset due to an error.
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index 0f08c262887..f64f6dce93a 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -18,20 +18,28 @@ package com.android.settings;
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 @@ public class ResetNetwork extends InstrumentedPreferenceFragment {
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 @@ public class ResetNetwork extends InstrumentedPreferenceFragment {
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 @@ public class ResetNetwork extends InstrumentedPreferenceFragment {
*/
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 @@ public class ResetNetwork extends InstrumentedPreferenceFragment {
}
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 58b82893199..53d9386643a 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.ConnectivityManager;
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.ims.ImsManager;
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 @@ public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
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 {
+ 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 @@ public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
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 @@ public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
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 @@ public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
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 00000000000..8a369695f2c
--- /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 00000000000..354cacf757c
--- /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());
+ }
+}