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 & 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());
+ }
+}