diff options
7 files changed, 286 insertions, 3 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 189e1b6ea9c..c68b9fa3e60 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -516,6 +516,11 @@ android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" android:theme="@style/Transparent" /> + <activity android:name=".network.telephony.DeleteEuiccSubscriptionDialogActivity" + android:exported="false" + android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" + android:theme="@style/Transparent" /> + <activity android:name="Settings$TetherSettingsActivity" android:label="@string/tether_settings_title_all" diff --git a/res/values/strings.xml b/res/values/strings.xml index c3d525609b3..c470809d17b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -11938,6 +11938,20 @@ <!-- Body text of error message indicating the device could not disable the mobile network, due to an unknown issue. [CHAR LIMIT=NONE] --> <string name="privileged_action_disable_fail_text">Something went wrong and your carrier could not be disabled.</string> + <!-- Strings for deleting eUICC subscriptions dialog activity --> + <!-- Title on confirmation dialog asking the user if they want to erase the downloaded SIM from the device. [CHAR_LIMIT=NONE] --> + <string name="erase_sim_dialog_title">Erase this downloaded SIM?</string> + <!-- Body text in confirmation dialog indicating what erasing a SIM entails. [CHAR_LIMIT=NONE] --> + <string name="erase_sim_dialog_text">Erasing this SIM removes <xliff:g id="carrier_name_a" example="Google Fi">%1$s</xliff:g> service from this device.\n\nService for <xliff:g id="carrier_name_b" example="Google Fi">%1$s</xliff:g> won\'t be canceled.</string> + <!-- Button label to erase the eSIM [CHAR_LIMIT=20] --> + <string name="erase_sim_confirm_button">Erase</string> + <!-- Status message indicating the device is in the process of erasing the SIM. [CHAR_LIMIT=NONE] --> + <string name="erasing_sim">Erasing SIM…</string> + <!-- Title of error message indicating the device could not erase the SIM. [CHAR_LIMIT=NONE] --> + <string name="erase_sim_fail_title">Can\'t erase SIM</string> + <!-- Body text of error message indicating the device could not erase the SIM due to an error. [CHAR_LIMIT=NONE] --> + <string name="erase_sim_fail_text">This SIM can\'t be erased due to an error.\n\nRestart your device and try again.</string> + <!-- Title for Network connection request Dialog [CHAR LIMIT=60] --> <string name="network_connection_request_dialog_title">Connect to device</string> <!-- Summary for Network connection request Dialog [CHAR LIMIT=NONE] --> diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java index ac21e12c232..e2c899789a2 100644 --- a/src/com/android/settings/network/SubscriptionUtil.java +++ b/src/com/android/settings/network/SubscriptionUtil.java @@ -31,12 +31,15 @@ import android.telephony.UiccSlotInfo; import androidx.annotation.VisibleForTesting; +import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity; import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; public class SubscriptionUtil { private static final String TAG = "SubscriptionUtil"; @@ -287,6 +290,11 @@ public class SubscriptionUtil { context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable)); } + /** Starts a dialog activity to handle eSIM deletion. */ + public static void startDeleteEuiccSubscriptionDialogActivity(Context context, int subId) { + context.startActivity(DeleteEuiccSubscriptionDialogActivity.getIntent(context, subId)); + } + /** * Finds and returns a subscription with a specific subscription ID. * @param subscriptionManager The ProxySubscriptionManager for accessing subscription @@ -332,4 +340,33 @@ public class SubscriptionUtil { || subscriptionManager.canManageSubscription(info); return hasCarrierPrivilegePermission; } + + /** + * Finds all the available subscriptions having the same group uuid as {@code subscriptionInfo}. + * @param subscriptionManager The SubscriptionManager for accessing subscription information + * @param subId The id of subscription + * @return a list of {@code SubscriptionInfo} which have the same group UUID. + */ + public static List<SubscriptionInfo> findAllSubscriptionsInGroup( + SubscriptionManager subscriptionManager, int subId) { + + SubscriptionInfo subscription = getSubById(subscriptionManager, subId); + if (subscription == null) { + return Collections.emptyList(); + } + ParcelUuid groupUuid = subscription.getGroupUuid(); + List<SubscriptionInfo> availableSubscriptions = + subscriptionManager.getAvailableSubscriptionInfoList(); + + if (availableSubscriptions == null + || availableSubscriptions.isEmpty() + || groupUuid == null) { + return Collections.singletonList(subscription); + } + + return availableSubscriptions + .stream() + .filter(sub -> sub.isEmbedded() && groupUuid.equals(sub.getGroupUuid())) + .collect(Collectors.toList()); + } } diff --git a/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java new file mode 100644 index 00000000000..6186de3be96 --- /dev/null +++ b/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2020 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.network.telephony; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.util.Log; + +import com.android.settings.R; +import com.android.settings.SidecarFragment; +import com.android.settings.network.SubscriptionUtil; + +import java.util.List; + +/** This dialog activity handles deleting eSIM profiles. */ +public class DeleteEuiccSubscriptionDialogActivity extends SubscriptionActionDialogActivity + implements SidecarFragment.Listener, ConfirmDialogFragment.OnConfirmListener { + + private static final String TAG = "DeleteEuiccSubscriptionDialogActivity"; + // Dialog tags + private static final int DIALOG_TAG_DELETE_SIM_CONFIRMATION = 1; + + /** + * Returns an intent of DeleteEuiccSubscriptionDialogActivity. + * + * @param context The context used to start the DeleteEuiccSubscriptionDialogActivity. + * @param subId The subscription ID of the subscription needs to be deleted. If the subscription + * belongs to a group of subscriptions, all subscriptions from the group will be deleted. + */ + public static Intent getIntent(Context context, int subId) { + Intent intent = new Intent(context, DeleteEuiccSubscriptionDialogActivity.class); + intent.putExtra(ARG_SUB_ID, subId); + return intent; + } + + private DeleteEuiccSubscriptionSidecar mDeleteEuiccSubscriptionSidecar; + private List<SubscriptionInfo> mSubscriptionsToBeDeleted; + private SubscriptionInfo mSubscriptionToBeDeleted; + private AlertDialog mDeleteSimConfirmDialog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); + mSubscriptionToBeDeleted = SubscriptionUtil.getSubById(mSubscriptionManager, subId); + mSubscriptionsToBeDeleted = + SubscriptionUtil.findAllSubscriptionsInGroup(mSubscriptionManager, subId); + mDeleteEuiccSubscriptionSidecar = DeleteEuiccSubscriptionSidecar.get(getFragmentManager()); + + if (mSubscriptionToBeDeleted == null || mDeleteEuiccSubscriptionSidecar == null) { + Log.e(TAG, "Cannot find subscription with sub ID: " + subId); + finish(); + return; + } + + if (savedInstanceState == null) { + showDeleteSimConfirmDialog(); + } + } + + @Override + protected void onResume() { + super.onResume(); + mDeleteEuiccSubscriptionSidecar.addListener(this); + } + + @Override + protected void onPause() { + mDeleteEuiccSubscriptionSidecar.removeListener(this); + super.onPause(); + } + + @Override + public void onStateChange(SidecarFragment fragment) { + if (fragment == mDeleteEuiccSubscriptionSidecar) { + handleDeleteEuiccSubscriptionSidecarStateChange(); + } + } + + @Override + public void onConfirm(int tag, boolean confirmed) { + if (!confirmed) { + finish(); + return; + } + + switch (tag) { + case DIALOG_TAG_DELETE_SIM_CONFIRMATION: + Log.i(TAG, "Subscription deletion confirmed"); + showProgressDialog(getString(R.string.erasing_sim)); + mDeleteEuiccSubscriptionSidecar.run(mSubscriptionsToBeDeleted); + break; + default: + Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag); + break; + } + } + + private void handleDeleteEuiccSubscriptionSidecarStateChange() { + switch (mDeleteEuiccSubscriptionSidecar.getState()) { + case SidecarFragment.State.SUCCESS: + Log.i(TAG, "Successfully delete the subscription."); + mDeleteEuiccSubscriptionSidecar.reset(); + dismissProgressDialog(); + finish(); + break; + case SidecarFragment.State.ERROR: + Log.e(TAG, "Failed to delete the subscription."); + mDeleteEuiccSubscriptionSidecar.reset(); + showErrorDialog( + getString(R.string.erase_sim_fail_title), + getString(R.string.erase_sim_fail_text)); + break; + } + } + + /* Displays the eSIM deleting confirmation dialog. */ + private void showDeleteSimConfirmDialog() { + ConfirmDialogFragment.show( + this, + ConfirmDialogFragment.OnConfirmListener.class, + DIALOG_TAG_DELETE_SIM_CONFIRMATION, + getString(R.string.erase_sim_dialog_title), + getString( + R.string.erase_sim_dialog_text, mSubscriptionToBeDeleted.getDisplayName()), + getString(R.string.erase_sim_confirm_button), + getString(R.string.cancel)); + } +} diff --git a/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionSidecar.java b/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionSidecar.java new file mode 100644 index 00000000000..f03f33fc1f1 --- /dev/null +++ b/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionSidecar.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 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.network.telephony; + +import android.app.FragmentManager; +import android.app.PendingIntent; +import android.telephony.SubscriptionInfo; +import android.telephony.euicc.EuiccManager; +import android.util.Log; + +import com.android.settings.SidecarFragment; + +import java.util.ArrayList; +import java.util.List; + +/** A headless fragment encapsulating long-running eSIM erasing operations. */ +public class DeleteEuiccSubscriptionSidecar extends EuiccOperationSidecar { + private static final String TAG = "DeleteEuiccSubscriptionSidecar"; + private static final String ACTION_DELETE_SUBSCRIPTION = + "com.android.settings.network.delete_subscription"; + + private List<SubscriptionInfo> mSubscriptions; + + @Override + public String getReceiverAction() { + return ACTION_DELETE_SUBSCRIPTION; + } + + /** Returns a DeleteEuiccSubscriptionSidecar sidecar instance. */ + public static DeleteEuiccSubscriptionSidecar get(FragmentManager fm) { + return SidecarFragment.get(fm, TAG, DeleteEuiccSubscriptionSidecar.class, null /* args */); + } + + /** Starts calling EuiccManager#deleteSubscription to delete the eSIM profile. */ + public void run(List<SubscriptionInfo> subscriptions) { + if (subscriptions == null || subscriptions.isEmpty()) { + throw new IllegalArgumentException("Subscriptions cannot be empty."); + } + + setState(State.RUNNING, Substate.UNUSED); + + mSubscriptions = new ArrayList<>(subscriptions); + deleteSubscription(); + } + + @Override + protected void onActionReceived() { + if (getResultCode() == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK + && !mSubscriptions.isEmpty()) { + // Continue to delete remaining subscriptions. + deleteSubscription(); + } else { + super.onActionReceived(); + } + } + + private void deleteSubscription() { + SubscriptionInfo subscription = mSubscriptions.remove(0); + PendingIntent intent = createCallbackIntent(); + Log.i(TAG, "Deleting subscription ID: " + subscription.getSubscriptionId()); + mEuiccManager.deleteSubscription(subscription.getSubscriptionId(), intent); + } +} diff --git a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java index 44187e486ae..3035a9f8c4a 100644 --- a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java +++ b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java @@ -81,9 +81,8 @@ public class DeleteSimProfilePreferenceController extends BasePreferenceControll } private void deleteSim() { - final Intent intent = new Intent(EuiccManager.ACTION_DELETE_SUBSCRIPTION_PRIVILEGED); - intent.putExtra(EuiccManager.EXTRA_SUBSCRIPTION_ID, mSubscriptionInfo.getSubscriptionId()); - mParentFragment.startActivityForResult(intent, mRequestCode); + SubscriptionUtil.startDeleteEuiccSubscriptionDialogActivity( + mContext, mSubscriptionInfo.getSubscriptionId()); // result handled in MobileNetworkSettings } diff --git a/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceControllerTest.java index 401a92e2be4..502e3febec8 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceControllerTest.java @@ -39,6 +39,7 @@ import com.android.settings.security.ConfirmSimDeletionPreferenceController; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -117,6 +118,7 @@ public class DeleteSimProfilePreferenceControllerTest { } @Test + @Ignore public void onPreferenceClick_startsIntent() { mController.init(SUB_ID, mFragment, REQUEST_CODE); mController.displayPreference(mScreen); |