Slot Change Receiver Migration
Implemented slot change cases when pSIM is inserted and removed.
Bug: 153811431
Bug: 170508680
Test: Manually tested
Change-Id: Ib0a96da1d7d702f7c64e75b929c73b8548f8e459
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8b3160f..8aabdb7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3669,6 +3669,26 @@
</intent-filter>
</receiver>
+ <activity
+ android:name=".sim.ChooseSimActivity"
+ android:theme="@style/GlifV3Theme.DayNight.NoActionBar"
+ android:launchMode="singleInstance"
+ android:exported="false"/>
+
+ <activity
+ android:name=".sim.SwitchToEsimConfirmDialogActivity"
+ android:exported="false"
+ android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
+ android:launchMode="singleInstance"
+ android:theme="@style/Transparent" />
+
+ <activity
+ android:name=".sim.DsdsDialogActivity"
+ android:exported="false"
+ android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
+ android:launchMode="singleInstance"
+ android:theme="@style/Transparent" />
+
<service android:name=".sim.SimNotificationService"
android:permission="android.permission.BIND_JOB_SERVICE" />
diff --git a/res/drawable/ic_network_signal_blue.xml b/res/drawable/ic_network_signal_blue.xml
new file mode 100644
index 0000000..3d7f63c
--- /dev/null
+++ b/res/drawable/ic_network_signal_blue.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="32dp"
+ android:viewportWidth="32"
+ android:viewportHeight="32">
+
+ <path
+ android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z" />
+ <path
+ android:fillColor="@color/homepage_generic_icon_background"
+ android:pathData="M24,5.33h1.33c0.74,0,1.33,0.6,1.33,1.33v18.67c0,0.74-0.6,1.33-1.33,1.33H24c-0.74,0-1.33-0.6-1.33-1.33
+V6.67C22.67,5.93,23.26,5.33,24,5.33z" />
+ <path
+ android:fillColor="@color/homepage_generic_icon_background"
+ android:pathData="M8,18.67h1.33c0.74,0,1.33,0.6,1.33,1.33v5.33c0,0.74-0.6,1.33-1.33,1.33H8c-0.74,0-1.33-0.6-1.33-1.33V20
+C6.67,19.26,7.26,18.67,8,18.67z" />
+ <path
+ android:fillColor="@color/homepage_generic_icon_background"
+ android:pathData="M16,12h1.33c0.74,0,1.33,0.6,1.33,1.33v12c0,0.74-0.6,1.33-1.33,1.33H16c-0.74,0-1.33-0.6-1.33-1.33v-12
+C14.67,12.6,15.26,12,16,12z" />
+</vector>
diff --git a/res/layout/choose_sim_activity.xml b/res/layout/choose_sim_activity.xml
new file mode 100644
index 0000000..05ab230
--- /dev/null
+++ b/res/layout/choose_sim_activity.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<com.google.android.setupdesign.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/glif_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:icon="@drawable/ic_network_signal_blue">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ style="@style/SudContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/subtitle_bottom_padding">
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/SudDescription.Glif"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <com.google.android.setupdesign.GlifRecyclerLayout
+ android:id="@+id/recycler_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:entries="@xml/items_multiple_carrier" />
+
+ </LinearLayout>
+</com.google.android.setupdesign.GlifLayout>
diff --git a/res/values-night/themes_suw.xml b/res/values-night/themes_suw.xml
index 16aba76..dea7b1a 100644
--- a/res/values-night/themes_suw.xml
+++ b/res/values-night/themes_suw.xml
@@ -21,6 +21,7 @@
<style name="GlifTheme.DayNight" parent="GlifTheme" />
<style name="GlifV2Theme.DayNight" parent="GlifV2Theme" />
<style name="GlifV3Theme.DayNight" parent="GlifV3Theme" />
+ <style name="GlifV3Theme.DayNight.NoActionBar" parent="GlifV3Theme.NoActionBar" />
<style name="GlifV2Theme.DayNight.Transparent" parent="GlifV2Theme.Transparent" />
<style name="GlifV3Theme.DayNight.Transparent" parent="GlifV3Theme.Transparent" />
<style name="SetupWizardTheme.DayNight.Transparent" parent="SetupWizardTheme.Transparent" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8f6be68..ab41aca 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -440,4 +440,7 @@
<!-- Text padding for EmptyTextSettings -->
<dimen name="empty_text_padding">24dp</dimen>
+
+ <!-- Choose SIM Activity dimens -->
+ <dimen name="subtitle_bottom_padding">24dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index dda89ee..65c514a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -12129,6 +12129,34 @@
<string name="post_dsds_reboot_notification_title_with_carrier"><xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> is active</string>
<!-- The body text of post DSDS reboot notification. [CHAR LIMIT=NONE] -->
<string name="post_dsds_reboot_notification_text">Tap to update SIM settings</string>
+ <!-- Title on a push notification indicating that the user's device switched to a new mobile network. [CHAR LIMIT=NONE] -->
+ <string name="switch_to_removable_notification">Switched to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
+ <!-- Title on a push notification indicating that the user's device switched to a new mobile network. [CHAR LIMIT=NONE] -->
+ <string name="switch_to_removable_notification_no_carrier_name">Switched to another carrier</string>
+ <!-- Message in a push notification indicating that the user's phone has connected to a different mobile network. [CHAR LIMIT=NONE] -->
+ <string name="network_changed_notification_text">Your mobile network has changed</string>
+
+ <!-- Strings for choose SIM activity -->
+ <!-- The title text of choose SIM activity. [CHAR LIMIT=NONE] -->
+ <string name="choose_sim_title">Choose a number to use</string>
+ <!-- The body text of choose SIM activity. [CHAR LIMIT=NONE] -->
+ <string name="choose_sim_text"><xliff:g id="number" example="2">%1$d</xliff:g> numbers are available on this device, but only one can be used at a time</string>
+ <!-- String indicating that we are activating the profile [CHAR LIMIT=NONE] -->
+ <string name="choose_sim_activating">Activating<xliff:g id="ellipsis" example="...">…</xliff:g></string>
+ <!-- String indicating that we failed to activate the selected profile [CHAR LIMIT=NONE] -->
+ <string name="choose_sim_could_not_activate">Couldn\u2019t be activated right now</string>
+ <!-- String indicating that the number for the specified profile is unknown [CHAR LIMIT=NONE] -->
+ <string name="choose_sim_item_summary_unknown">Unknown number</string>
+
+ <!-- Strings for switch SIM confirmation dialog. -->
+ <!-- The title text of switch SIM confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="switch_sim_dialog_title">Use <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
+ <!-- The body text of switch SIM confirmation dialog. [CHAR LIMIT=NONE] -->
+ <string name="switch_sim_dialog_text"><xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> will be used for mobile data, calls, and SMS.</string>
+ <!-- The title text of skip sim switch dialog. [CHAR LIMIT=NONE] -->
+ <string name="switch_sim_dialog_no_switch_title">No active SIMs available</string>
+ <!-- The body text of skip sim switch dialog. [CHAR LIMIT=NONE] -->
+ <string name="switch_sim_dialog_no_switch_text">To use mobile data, call features, and SMS at a later time, go to your network settings</string>
<!-- Button label of the removable sim card. [CHAR LIMIT=NONE] -->
<string name="sim_card_label">SIM card</string>
diff --git a/res/values/themes_suw.xml b/res/values/themes_suw.xml
index 959ffb3..b286a51 100644
--- a/res/values/themes_suw.xml
+++ b/res/values/themes_suw.xml
@@ -133,6 +133,14 @@
<item name="*android:lockPatternStyle">@style/LockPatternStyle.Setup</item>
</style>
+ <style name="GlifV3Theme.Light.NoActionBar" parent="GlifV3Theme.Light">
+ <item name="android:windowActionBar">false</item>
+ </style>
+
+ <style name="GlifV3Theme.NoActionBar" parent="GlifV3Theme">
+ <item name="android:windowActionBar">false</item>
+ </style>
+
<style name="GlifV2Theme.Transparent">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
@@ -216,6 +224,7 @@
<style name="GlifTheme.DayNight" parent="GlifTheme.Light" />
<style name="GlifV2Theme.DayNight" parent="GlifV2Theme.Light" />
<style name="GlifV3Theme.DayNight" parent="GlifV3Theme.Light" />
+ <style name="GlifV3Theme.DayNight.NoActionBar" parent="GlifV3Theme.Light.NoActionBar" />
<style name="GlifV2Theme.DayNight.Transparent" parent="GlifV2Theme.Light.Transparent" />
<style name="GlifV3Theme.DayNight.Transparent" parent="GlifV3Theme.Light.Transparent" />
<style name="SetupWizardTheme.DayNight.Transparent" parent="SetupWizardTheme.Light.Transparent" />
diff --git a/res/xml/items_multiple_carrier.xml b/res/xml/items_multiple_carrier.xml
new file mode 100644
index 0000000..b65e0cb
--- /dev/null
+++ b/res/xml/items_multiple_carrier.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<ItemGroup xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index ff5de3e..ba0ae99 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -24,15 +24,18 @@
import android.annotation.Nullable;
import android.content.Context;
import android.os.ParcelUuid;
+import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotInfo;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.telephony.MccTable;
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
import com.android.settingslib.DeviceInfoUtils;
@@ -514,4 +517,64 @@
.filter(sub -> sub.isEmbedded() && groupUuid.equals(sub.getGroupUuid()))
.collect(Collectors.toList());
}
+
+ /** Returns the formatted phone number of a subscription. */
+ @Nullable
+ public static String getFormattedPhoneNumber(
+ Context context, SubscriptionInfo subscriptionInfo) {
+ if (subscriptionInfo == null) {
+ Log.e(TAG, "Invalid subscription.");
+ return null;
+ }
+
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ String rawPhoneNumber =
+ telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId());
+ String countryIso = MccTable.countryCodeForMcc(subscriptionInfo.getMccString());
+ if (TextUtils.isEmpty(rawPhoneNumber)) {
+ return null;
+ }
+ return PhoneNumberUtils.formatNumber(rawPhoneNumber, countryIso);
+ }
+
+ /**
+ * Returns the subscription on a removable sim card. The device does not need to be on removable
+ * slot.
+ */
+ @Nullable
+ public static SubscriptionInfo getFirstRemovableSubscription(Context context) {
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ SubscriptionManager subscriptionManager =
+ context.getSystemService(SubscriptionManager.class);
+ List<UiccCardInfo> cardInfos = telephonyManager.getUiccCardsInfo();
+ if (cardInfos == null) {
+ Log.w(TAG, "UICC cards info list is empty.");
+ return null;
+ }
+ List<SubscriptionInfo> allSubscriptions = subscriptionManager.getAllSubscriptionInfoList();
+ if (allSubscriptions == null) {
+ Log.w(TAG, "All subscription info list is empty.");
+ return null;
+ }
+ for (UiccCardInfo cardInfo : cardInfos) {
+ if (cardInfo == null) {
+ Log.w(TAG, "Got null card.");
+ continue;
+ }
+ if (!cardInfo.isRemovable()
+ || cardInfo.getCardId() == TelephonyManager.UNSUPPORTED_CARD_ID) {
+ Log.i(TAG, "Skip embedded card or invalid cardId on slot: "
+ + cardInfo.getSlotIndex());
+ continue;
+ }
+ Log.i(TAG, "Target removable cardId :" + cardInfo.getCardId());
+ for (SubscriptionInfo subInfo : allSubscriptions) {
+ // Match the removable card id with subscription card id.
+ if (cardInfo.getCardId() == subInfo.getCardId()) {
+ return subInfo;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
index c1be63c..d3b2af6 100644
--- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
@@ -177,10 +177,7 @@
showRebootConfirmDialog();
return;
}
- Log.i(
- TAG,
- "Enabling DSDS without rebooting. "
- + getString(R.string.sim_action_enabling_sim_without_carrier_name));
+ Log.i(TAG, "Enabling DSDS without rebooting.");
showProgressDialog(
getString(R.string.sim_action_enabling_sim_without_carrier_name));
mEnableMultiSimSidecar.run(NUM_OF_SIMS_FOR_DSDS);
@@ -272,7 +269,7 @@
case SidecarFragment.State.ERROR:
mEnableMultiSimSidecar.reset();
Log.i(TAG, "Failed to switch to DSDS without rebooting.");
- ProgressDialogFragment.dismiss(getFragmentManager());
+ dismissProgressDialog();
showErrorDialog(
getString(R.string.dsds_activation_failure_title),
getString(R.string.dsds_activation_failure_body_msg2));
@@ -290,7 +287,7 @@
Log.i(TAG, "DSDS enabled, start to enable pSIM profile.");
handleTogglePsimAction();
- ProgressDialogFragment.dismiss(getFragmentManager());
+ dismissProgressDialog();
finish();
}
diff --git a/src/com/android/settings/sim/ChooseSimActivity.java b/src/com/android/settings/sim/ChooseSimActivity.java
new file mode 100644
index 0000000..f8bdc30
--- /dev/null
+++ b/src/com/android/settings/sim/ChooseSimActivity.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2021 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.sim;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.SidecarFragment;
+import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
+import com.android.settings.network.SwitchToRemovableSlotSidecar;
+import com.android.settings.network.UiccSlotUtil;
+
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.GlifRecyclerLayout;
+import com.google.android.setupdesign.items.Dividable;
+import com.google.android.setupdesign.items.IItem;
+import com.google.android.setupdesign.items.Item;
+import com.google.android.setupdesign.items.ItemGroup;
+import com.google.android.setupdesign.items.RecyclerItemAdapter;
+import com.google.android.setupdesign.view.HeaderRecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Activity to show a list of profiles for user to choose. */
+public class ChooseSimActivity extends Activity
+ implements RecyclerItemAdapter.OnItemSelectedListener, SidecarFragment.Listener {
+ // Whether there is a pSIM profile in the selection list.
+ public static final String KEY_HAS_PSIM = "has_psim";
+ // After the user selects eSIM profile, whether continue to show Mobile Network Settings screen
+ // to select other preferences.
+ // Note: KEY_NO_PSIM_CONTINUE_TO_SETTINGS and mNoPsimContinueToSettings are not used for now
+ // for UI changes. We may use them in the future.
+ public static final String KEY_NO_PSIM_CONTINUE_TO_SETTINGS = "no_psim_continue_to_settings";
+
+ private static final String TAG = "ChooseSimActivity";
+ private static final int INDEX_PSIM = -1;
+ private static final String STATE_SELECTED_INDEX = "selected_index";
+ private static final String STATE_IS_SWITCHING = "is_switching";
+
+ private boolean mHasPsim;
+ private boolean mNoPsimContinueToSettings;
+ private ArrayList<SubscriptionInfo> mEmbeddedSubscriptions = new ArrayList<>();
+ private SubscriptionInfo mRemovableSubscription = null;
+
+ private ItemGroup mItemGroup;
+ private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar;
+ private SwitchToRemovableSlotSidecar mSwitchToRemovableSlotSidecar;
+
+ // Variables have states.
+ private int mSelectedItemIndex;
+ private boolean mIsSwitching;
+
+ /** Returns an intent of {@code ChooseSimActivity} */
+ public static Intent getIntent(Context context) {
+ return new Intent(context, ChooseSimActivity.class);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.choose_sim_activity);
+
+ Intent intent = getIntent();
+ mHasPsim = intent.getBooleanExtra(KEY_HAS_PSIM, false);
+ mNoPsimContinueToSettings = intent.getBooleanExtra(KEY_NO_PSIM_CONTINUE_TO_SETTINGS, false);
+
+ updateSubscriptions();
+
+ if (mEmbeddedSubscriptions.size() == 0) {
+ Log.e(TAG, "Unable to find available eSIM subscriptions.");
+ finish();
+ return;
+ }
+
+ if (savedInstanceState != null) {
+ mSelectedItemIndex = savedInstanceState.getInt(STATE_SELECTED_INDEX);
+ mIsSwitching = savedInstanceState.getBoolean(STATE_IS_SWITCHING);
+ }
+
+ GlifLayout layout = findViewById(R.id.glif_layout);
+ TextView textView = findViewById(R.id.subtitle);
+ int subscriptionCount = mEmbeddedSubscriptions.size();
+ if (mHasPsim) { // Choose a number to use
+ subscriptionCount++;
+ }
+ layout.setHeaderText(getString(R.string.choose_sim_title));
+ textView.setText(getString(R.string.choose_sim_text, subscriptionCount));
+
+ displaySubscriptions();
+
+ mSwitchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(getFragmentManager());
+ mSwitchToEuiccSubscriptionSidecar =
+ SwitchToEuiccSubscriptionSidecar.get(getFragmentManager());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mSwitchToRemovableSlotSidecar.addListener(this);
+ mSwitchToEuiccSubscriptionSidecar.addListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ mSwitchToEuiccSubscriptionSidecar.removeListener(this);
+ mSwitchToRemovableSlotSidecar.removeListener(this);
+ super.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putInt(STATE_SELECTED_INDEX, mSelectedItemIndex);
+ outState.putBoolean(STATE_IS_SWITCHING, mIsSwitching);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onItemSelected(IItem item) {
+ if (mIsSwitching) {
+ // If we already selected an item, do not try to switch to another one.
+ return;
+ }
+ mIsSwitching = true;
+ Item subItem = (Item) item;
+ subItem.setSummary(getString(R.string.choose_sim_activating));
+ mSelectedItemIndex = subItem.getId();
+ if (mSelectedItemIndex == INDEX_PSIM) {
+ Log.i(TAG, "Ready to switch to pSIM slot.");
+ mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID);
+ } else {
+ Log.i(TAG, "Ready to switch to eSIM subscription with index: " + mSelectedItemIndex);
+ mSwitchToEuiccSubscriptionSidecar.run(
+ mEmbeddedSubscriptions.get(mSelectedItemIndex).getSubscriptionId());
+ }
+ }
+
+ @Override
+ public void onStateChange(SidecarFragment fragment) {
+ if (fragment == mSwitchToRemovableSlotSidecar) {
+ switch (mSwitchToRemovableSlotSidecar.getState()) {
+ case SidecarFragment.State.SUCCESS:
+ mSwitchToRemovableSlotSidecar.reset();
+ Log.i(TAG, "Switch slot successfully.");
+ SubscriptionManager subMgr = getSystemService(SubscriptionManager.class);
+ if (subMgr.canDisablePhysicalSubscription()) {
+ SubscriptionInfo removableSub =
+ SubscriptionUtil.getFirstRemovableSubscription(this);
+ if (removableSub != null) {
+ subMgr.setUiccApplicationsEnabled(
+ removableSub.getSubscriptionId(), true);
+ }
+ }
+ finish();
+ break;
+ case SidecarFragment.State.ERROR:
+ mSwitchToRemovableSlotSidecar.reset();
+ Log.e(TAG, "Failed to switch slot in ChooseSubscriptionsActivity.");
+ handleEnableRemovableSimError();
+ // We don't call finish() and just stay on this page.
+ break;
+ }
+ } else if (fragment == mSwitchToEuiccSubscriptionSidecar) {
+ switch (mSwitchToEuiccSubscriptionSidecar.getState()) {
+ case SidecarFragment.State.SUCCESS:
+ mSwitchToEuiccSubscriptionSidecar.reset();
+ if (mNoPsimContinueToSettings) {
+ // Currently, there shouldn't be a case that mNoPsimContinueToSettings is
+ // true. If this can be true in the future, we should finish() this page
+ // and direct to Settings page here.
+ Log.e(
+ TAG,
+ "mNoPsimContinueToSettings is true which is not supported for"
+ + " now.");
+ } else {
+ Log.i(TAG, "User finished selecting eSIM profile.");
+ finish();
+ }
+ break;
+ case SidecarFragment.State.ERROR:
+ mSwitchToEuiccSubscriptionSidecar.reset();
+ Log.e(TAG, "Failed to switch subscription in ChooseSubscriptionsActivity.");
+ Item item = (Item) mItemGroup.getItemAt(mSelectedItemIndex);
+ item.setEnabled(false);
+ item.setSummary(getString(R.string.choose_sim_could_not_activate));
+ mIsSwitching = false;
+ // We don't call finish() and just stay on this page.
+ break;
+ }
+ }
+ }
+
+ private void displaySubscriptions() {
+ View rootView = findViewById(android.R.id.content);
+ GlifRecyclerLayout layout = rootView.findViewById(R.id.recycler_list);
+ RecyclerItemAdapter adapter = (RecyclerItemAdapter) layout.getAdapter();
+ adapter.setOnItemSelectedListener(this);
+ mItemGroup = (ItemGroup) adapter.getRootItemHierarchy();
+
+ // Display pSIM profile.
+ if (mHasPsim) {
+ Item item = new DisableableItem();
+ // Title
+ CharSequence title = null;
+ if (mRemovableSubscription != null) {
+ title =
+ SubscriptionUtil.getUniqueSubscriptionDisplayName(
+ mRemovableSubscription.getSubscriptionId(), this);
+ }
+ item.setTitle(TextUtils.isEmpty(title) ? getString(R.string.sim_card_label) : title);
+
+ if (mIsSwitching && mSelectedItemIndex == INDEX_PSIM) {
+ item.setSummary(getString(R.string.choose_sim_activating));
+ } else {
+ // Phone number
+ String phoneNumber =
+ SubscriptionUtil.getFormattedPhoneNumber(this, mRemovableSubscription);
+ item.setSummary(TextUtils.isEmpty(phoneNumber) ? "" : phoneNumber);
+ }
+
+ // pSIM profile has index -1.
+ item.setId(INDEX_PSIM);
+ mItemGroup.addChild(item);
+ }
+
+ // Display all eSIM profiles.
+ int index = 0;
+ for (SubscriptionInfo sub : mEmbeddedSubscriptions) {
+ Item item = new DisableableItem();
+ CharSequence title =
+ SubscriptionUtil.getUniqueSubscriptionDisplayName(
+ sub.getSubscriptionId(), this);
+ item.setTitle(TextUtils.isEmpty(title) ? sub.getDisplayName() : title);
+ if (mIsSwitching && mSelectedItemIndex == index) {
+ item.setSummary(getString(R.string.choose_sim_activating));
+ } else {
+ String phoneNumber = SubscriptionUtil.getFormattedPhoneNumber(this, sub);
+ item.setSummary(TextUtils.isEmpty(phoneNumber) ? "" : phoneNumber);
+ }
+ item.setId(index++);
+ mItemGroup.addChild(item);
+ }
+
+ // This removes the unused header artifact from GlifRecyclerLayout.
+ HeaderRecyclerView rv = (HeaderRecyclerView) layout.getRecyclerView();
+ rv.getHeader().setVisibility(View.GONE);
+ }
+
+ private void updateSubscriptions() {
+ List<SubscriptionInfo> subscriptions =
+ SubscriptionUtil.getSelectableSubscriptionInfoList(this);
+ if (subscriptions != null) {
+ for (SubscriptionInfo sub : subscriptions) {
+ if (sub == null) {
+ continue;
+ }
+ if (sub.isEmbedded()) {
+ mEmbeddedSubscriptions.add(sub);
+ } else {
+ mRemovableSubscription = sub;
+ }
+ }
+ }
+ }
+
+ private void handleEnableRemovableSimError() {
+ // mSelectedItemIndex will be -1 if pSIM is selected. Since pSIM is always be
+ // listed at index 0, we change the itemIndex to 0 if pSIM is selected.
+ int itemIndex = mSelectedItemIndex == INDEX_PSIM ? 0 : mSelectedItemIndex;
+ Item item = (Item) mItemGroup.getItemAt(itemIndex);
+ item.setEnabled(false);
+ item.setSummary(getString(R.string.choose_sim_could_not_activate));
+ mIsSwitching = false;
+ }
+
+ class DisableableItem extends Item implements Dividable {
+ @Override
+ public boolean isDividerAllowedAbove() {
+ return true;
+ }
+
+ @Override
+ public boolean isDividerAllowedBelow() {
+ return true;
+ }
+
+ @Override
+ public void onBindView(View view) {
+ super.onBindView(view);
+ TextView title = view.findViewById(R.id.sud_items_title);
+ TextView summary = view.findViewById(R.id.sud_items_summary);
+ title.setEnabled(isEnabled());
+ summary.setEnabled(isEnabled());
+ }
+ }
+}
diff --git a/src/com/android/settings/sim/DsdsDialogActivity.java b/src/com/android/settings/sim/DsdsDialogActivity.java
new file mode 100644
index 0000000..1390c81
--- /dev/null
+++ b/src/com/android/settings/sim/DsdsDialogActivity.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2021 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.sim;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.SidecarFragment;
+import com.android.settings.network.EnableMultiSimSidecar;
+import com.android.settings.network.telephony.ConfirmDialogFragment;
+import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
+
+/** Activity to show the enabling DSDS dialog. */
+public class DsdsDialogActivity extends SubscriptionActionDialogActivity
+ implements SidecarFragment.Listener, ConfirmDialogFragment.OnConfirmListener {
+
+ private static final String TAG = "DsdsDialogActivity";
+ // Dialog tags
+ private static final int DIALOG_TAG_ENABLE_DSDS_CONFIRMATION = 1;
+ private static final int DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION = 2;
+ // Number of SIMs for DSDS
+ private static final int NUM_OF_SIMS_FOR_DSDS = 2;
+
+ private EnableMultiSimSidecar mEnableMultiSimSidecar;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mEnableMultiSimSidecar = EnableMultiSimSidecar.get(getFragmentManager());
+ if (savedInstanceState == null) {
+ showEnableDsdsConfirmDialog();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mEnableMultiSimSidecar.addListener(this);
+ }
+
+ @Override
+ protected void onPause() {
+ mEnableMultiSimSidecar.removeListener(this);
+ super.onPause();
+ }
+
+ @Override
+ public void onStateChange(SidecarFragment fragment) {
+ if (fragment == mEnableMultiSimSidecar) {
+ switch (fragment.getState()) {
+ case SidecarFragment.State.SUCCESS:
+ mEnableMultiSimSidecar.reset();
+ Log.i(TAG, "Enabled DSDS successfully");
+ dismissProgressDialog();
+ finish();
+ break;
+ case SidecarFragment.State.ERROR:
+ mEnableMultiSimSidecar.reset();
+ Log.e(TAG, "Failed to enable DSDS");
+ dismissProgressDialog();
+ showErrorDialog(
+ getString(R.string.dsds_activation_failure_title),
+ getString(R.string.dsds_activation_failure_body_msg2));
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onConfirm(int tag, boolean confirmed) {
+ if (!confirmed) {
+ Log.i(TAG, "User cancel the dialog to enable DSDS.");
+ startChooseSimActivity();
+ return;
+ }
+
+ TelephonyManager telephonyManager = getSystemService(TelephonyManager.class);
+ switch (tag) {
+ case DIALOG_TAG_ENABLE_DSDS_CONFIRMATION:
+ if (telephonyManager.doesSwitchMultiSimConfigTriggerReboot()) {
+ Log.i(TAG, "Device does not support reboot free DSDS.");
+ showRebootConfirmDialog();
+ return;
+ }
+ Log.i(TAG, "Enabling DSDS without rebooting.");
+ showProgressDialog(
+ getString(R.string.sim_action_enabling_sim_without_carrier_name));
+ mEnableMultiSimSidecar.run(NUM_OF_SIMS_FOR_DSDS);
+ break;
+ case DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION:
+ Log.i(TAG, "User confirmed reboot to enable DSDS.");
+ SimActivationNotifier.setShowSimSettingsNotification(this, true);
+ telephonyManager.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS);
+ break;
+ default:
+ Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag);
+ break;
+ }
+ }
+
+ private void showEnableDsdsConfirmDialog() {
+ ConfirmDialogFragment.show(
+ this,
+ ConfirmDialogFragment.OnConfirmListener.class,
+ DIALOG_TAG_ENABLE_DSDS_CONFIRMATION,
+ getString(R.string.sim_action_enable_dsds_title),
+ getString(R.string.sim_action_enable_dsds_text),
+ getString(R.string.sim_action_continue),
+ getString(R.string.sim_action_no_thanks));
+ }
+
+ private void showRebootConfirmDialog() {
+ ConfirmDialogFragment.show(
+ this,
+ ConfirmDialogFragment.OnConfirmListener.class,
+ DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION,
+ getString(R.string.sim_action_restart_title),
+ getString(R.string.sim_action_enable_dsds_text),
+ getString(R.string.sim_action_reboot),
+ getString(R.string.cancel));
+ }
+
+ private void startChooseSimActivity() {
+ Intent intent = ChooseSimActivity.getIntent(this);
+ intent.putExtra(ChooseSimActivity.KEY_HAS_PSIM, true);
+ startActivity(intent);
+ finish();
+ }
+}
diff --git a/src/com/android/settings/sim/SimActivationNotifier.java b/src/com/android/settings/sim/SimActivationNotifier.java
index 4a4edef..a38816a 100644
--- a/src/com/android/settings/sim/SimActivationNotifier.java
+++ b/src/com/android/settings/sim/SimActivationNotifier.java
@@ -26,8 +26,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -40,6 +42,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import javax.annotation.Nullable;
+
/**
* This class manages the notification of SIM activation notification including creating and
* canceling the notifications.
@@ -48,21 +52,26 @@
private static final String TAG = "SimActivationNotifier";
private static final String SIM_SETUP_CHANNEL_ID = "sim_setup";
+ private static final String SWITCH_SLOT_CHANNEL_ID = "carrier_switching";
private static final String SIM_PREFS = "sim_prefs";
private static final String KEY_SHOW_SIM_SETTINGS_NOTIFICATION =
"show_sim_settings_notification";
public static final int SIM_ACTIVATION_NOTIFICATION_ID = 1;
+ public static final int SWITCH_TO_REMOVABLE_SLOT_NOTIFICATION_ID = 2;
/** Notification types */
@Retention(RetentionPolicy.SOURCE)
@IntDef(
value = {
NotificationType.NETWORK_CONFIG,
+ NotificationType.SWITCH_TO_REMOVABLE_SLOT,
})
public @interface NotificationType {
// The notification to remind users to config network Settings.
int NETWORK_CONFIG = 1;
+ // The notification to notify users that the device is switched to the removable slot.
+ int SWITCH_TO_REMOVABLE_SLOT = 2;
}
private final Context mContext;
@@ -104,13 +113,7 @@
/** Sends a push notification for the SIM activation. It should be called after DSDS reboot. */
public void sendNetworkConfigNotification() {
- SubscriptionManager subscriptionManager =
- mContext.getSystemService(SubscriptionManager.class);
- SubscriptionInfo activeRemovableSub =
- SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
- .filter(sub -> !sub.isEmbedded())
- .findFirst()
- .orElse(null);
+ SubscriptionInfo activeRemovableSub = getActiveRemovableSub();
if (activeRemovableSub == null) {
Log.e(TAG, "No removable subscriptions found. Do not show notification.");
@@ -143,4 +146,65 @@
.setAutoCancel(true);
mNotificationManager.notify(SIM_ACTIVATION_NOTIFICATION_ID, builder.build());
}
+
+ /** Sends a push notification for switching to the removable slot. */
+ public void sendSwitchedToRemovableSlotNotification() {
+ String carrierName = getActiveCarrierName();
+ Intent clickIntent = new Intent(mContext, Settings.MobileNetworkListActivity.class);
+ TaskStackBuilder stackBuilder =
+ TaskStackBuilder.create(mContext).addNextIntent(clickIntent);
+ PendingIntent contentIntent =
+ stackBuilder.getPendingIntent(
+ 0 /* requestCode */, PendingIntent.FLAG_UPDATE_CURRENT);
+ String titleText =
+ TextUtils.isEmpty(carrierName)
+ ? mContext.getString(
+ R.string.switch_to_removable_notification_no_carrier_name)
+ : mContext.getString(
+ R.string.switch_to_removable_notification, carrierName);
+ Notification.Builder builder =
+ new Notification.Builder(mContext, SWITCH_SLOT_CHANNEL_ID)
+ .setContentTitle(titleText)
+ .setContentText(
+ mContext.getString(R.string.network_changed_notification_text))
+ .setContentIntent(contentIntent)
+ .setSmallIcon(R.drawable.ic_sim_alert)
+ .setColor(
+ mContext.getResources()
+ .getColor(
+ R.color.homepage_generic_icon_background,
+ null /* theme */))
+ .setAutoCancel(true);
+ mNotificationManager.notify(SWITCH_TO_REMOVABLE_SLOT_NOTIFICATION_ID, builder.build());
+ }
+
+ @Nullable
+ private SubscriptionInfo getActiveRemovableSub() {
+ SubscriptionManager subscriptionManager =
+ mContext.getSystemService(SubscriptionManager.class);
+ return SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
+ .filter(sub -> !sub.isEmbedded())
+ .findFirst()
+ .orElse(null);
+ }
+
+ @Nullable
+ private String getActiveCarrierName() {
+ CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+ TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class);
+ String telName = telManager.getSimOperatorName();
+ if (configManager != null && configManager.getConfig() != null) {
+ boolean override =
+ configManager
+ .getConfig()
+ .getBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL);
+ String configName =
+ configManager
+ .getConfig()
+ .getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+
+ return override || TextUtils.isEmpty(telName) ? configName : telName;
+ }
+ return telName;
+ }
}
diff --git a/src/com/android/settings/sim/SimNotificationService.java b/src/com/android/settings/sim/SimNotificationService.java
index 303c21d..0f52c8b 100644
--- a/src/com/android/settings/sim/SimNotificationService.java
+++ b/src/com/android/settings/sim/SimNotificationService.java
@@ -35,6 +35,7 @@
/**
* Schedules a service to send SIM push notifications.
+ *
* @param context
* @param notificationType indicates which SIM notification to send.
*/
@@ -67,6 +68,9 @@
SimActivationNotifier.setShowSimSettingsNotification(this, false);
new SimActivationNotifier(this).sendNetworkConfigNotification();
break;
+ case SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT:
+ new SimActivationNotifier(this).sendSwitchedToRemovableSlotNotification();
+ break;
default:
Log.e(TAG, "Invalid notification type: " + notificationType);
break;
diff --git a/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java b/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java
new file mode 100644
index 0000000..385deff
--- /dev/null
+++ b/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 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.sim;
+
+import android.os.Bundle;
+import android.telephony.SubscriptionInfo;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.SidecarFragment;
+import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
+import com.android.settings.network.telephony.AlertDialogFragment;
+import com.android.settings.network.telephony.ConfirmDialogFragment;
+import com.android.settings.network.telephony.SubscriptionActionDialogActivity;
+
+/**
+ * Starts a confirm dialog asking the user to switch to the eSIM slot/subscription. The caller needs
+ * to pass in the current enabled eSIM subscription, which is also the subscription to switch to.
+ */
+public class SwitchToEsimConfirmDialogActivity extends SubscriptionActionDialogActivity
+ implements SidecarFragment.Listener, ConfirmDialogFragment.OnConfirmListener {
+
+ public static final String KEY_SUB_TO_ENABLE = "sub_to_enable";
+
+ private static final String TAG = "SwitchToEsimConfirmDialogActivity";
+ private static final int TAG_CONFIRM = 1;
+
+ private SubscriptionInfo mSubToEnabled = null;
+ private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mSubToEnabled = getIntent().getParcelableExtra(KEY_SUB_TO_ENABLE);
+ mSwitchToEuiccSubscriptionSidecar =
+ SwitchToEuiccSubscriptionSidecar.get(getFragmentManager());
+
+ if (mSubToEnabled == null) {
+ Log.e(TAG, "Cannot find SIM to enable.");
+ finish();
+ return;
+ }
+
+ if (savedInstanceState == null) {
+ ConfirmDialogFragment.show(
+ this,
+ ConfirmDialogFragment.OnConfirmListener.class,
+ TAG_CONFIRM,
+ getString(R.string.switch_sim_dialog_title, mSubToEnabled.getDisplayName()),
+ getString(R.string.switch_sim_dialog_text, mSubToEnabled.getDisplayName()),
+ getString(R.string.okay),
+ getString(R.string.cancel));
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mSwitchToEuiccSubscriptionSidecar.addListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ mSwitchToEuiccSubscriptionSidecar.removeListener(this);
+ super.onPause();
+ }
+
+ @Override
+ public void onStateChange(SidecarFragment fragment) {
+ if (fragment == mSwitchToEuiccSubscriptionSidecar) {
+ switch (mSwitchToEuiccSubscriptionSidecar.getState()) {
+ case SidecarFragment.State.SUCCESS:
+ mSwitchToEuiccSubscriptionSidecar.reset();
+ Log.i(TAG, "Successfully switched to eSIM slot.");
+ dismissProgressDialog();
+ finish();
+ break;
+ case SidecarFragment.State.ERROR:
+ mSwitchToEuiccSubscriptionSidecar.reset();
+ Log.e(TAG, "Failed switching to eSIM slot.");
+ dismissProgressDialog();
+ finish();
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onConfirm(int tag, boolean confirmed) {
+ if (!confirmed) {
+ AlertDialogFragment.show(
+ this,
+ getString(R.string.switch_sim_dialog_no_switch_title),
+ getString(R.string.switch_sim_dialog_no_switch_text));
+ return;
+ }
+ Log.i(TAG, "User confirmed to switch to embedded slot.");
+ mSwitchToEuiccSubscriptionSidecar.run(mSubToEnabled.getSubscriptionId());
+ showProgressDialog(
+ getString(
+ R.string.sim_action_switch_sub_dialog_progress,
+ mSubToEnabled.getDisplayName()));
+ }
+}
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
index 814f1a4..c092428 100644
--- a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
@@ -19,6 +19,7 @@
import static android.content.Context.MODE_PRIVATE;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Looper;
import android.provider.Settings;
@@ -29,6 +30,13 @@
import android.util.Log;
import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.UiccSlotUtil;
+import com.android.settings.network.UiccSlotsException;
+import com.android.settings.sim.ChooseSimActivity;
+import com.android.settings.sim.DsdsDialogActivity;
+import com.android.settings.sim.SimActivationNotifier;
+import com.android.settings.sim.SimNotificationService;
+import com.android.settings.sim.SwitchToEsimConfirmDialogActivity;
import com.google.common.collect.ImmutableList;
@@ -121,14 +129,13 @@
return;
}
- if (!hasActiveEsimSubscription()) {
- if (mTelMgr.isMultiSimEnabled()) {
+ if (hasActiveEsimSubscription()) {
+ if (mTelMgr.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
Log.i(TAG, "Enabled profile exists. DSDS condition satisfied.");
- // TODO(b/170508680): Display DSDS dialog to ask users whether to enable DSDS.
+ startDsdsDialogActivity();
} else {
Log.i(TAG, "Enabled profile exists. DSDS condition not satisfied.");
- // TODO(b/170508680): Display Choose a number to use screen for subscription
- // selection.
+ startChooseSimActivity(true);
}
return;
}
@@ -137,7 +144,15 @@
TAG,
"No enabled eSIM profile. Ready to switch to removable slot and show"
+ " notification.");
- // TODO(b/170508680): Switch the slot to the removebale slot and show the notification.
+ try {
+ UiccSlotUtil.switchToRemovableSlot(
+ UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID, mContext.getApplicationContext());
+ } catch (UiccSlotsException e) {
+ Log.e(TAG, "Failed to switch to removable slot.");
+ return;
+ }
+ SimNotificationService.scheduleSimNotification(
+ mContext, SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT);
}
private void handleSimRemove(UiccSlotInfo removableSlotInfo) {
@@ -160,14 +175,14 @@
// profile.
if (groupedEmbeddedSubscriptions.size() == 1) {
Log.i(TAG, "Only 1 eSIM profile found. Ask user's consent to switch.");
- // TODO(b/170508680): Display a dialog to ask users to switch.
+ startSwitchSlotConfirmDialogActivity(groupedEmbeddedSubscriptions.get(0));
return;
}
// If there are more than 1 eSIM profiles installed, we show a screen to let users to choose
// the number they want to use.
Log.i(TAG, "Multiple eSIM profiles found. Ask user which subscription to use.");
- // TODO(b/170508680): Display a dialog to ask user which SIM to switch.
+ startChooseSimActivity(false);
}
private int getLastRemovableSimSlotState(Context context) {
@@ -225,5 +240,25 @@
.collect(Collectors.toList()));
}
+ private void startChooseSimActivity(boolean psimInserted) {
+ Intent intent = ChooseSimActivity.getIntent(mContext);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(ChooseSimActivity.KEY_HAS_PSIM, psimInserted);
+ mContext.startActivity(intent);
+ }
+
+ private void startSwitchSlotConfirmDialogActivity(SubscriptionInfo subscriptionInfo) {
+ Intent intent = new Intent(mContext, SwitchToEsimConfirmDialogActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(SwitchToEsimConfirmDialogActivity.KEY_SUB_TO_ENABLE, subscriptionInfo);
+ mContext.startActivity(intent);
+ }
+
+ private void startDsdsDialogActivity() {
+ Intent intent = new Intent(mContext, DsdsDialogActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+
private SimSlotChangeHandler() {}
}
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
index a730dd1..563b4ef 100644
--- a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
@@ -48,14 +48,16 @@
return;
}
+ final PendingResult pendingResult = goAsync();
ThreadUtils.postOnBackgroundThread(
() -> {
synchronized (mLock) {
if (!shouldHandleSlotChange(context)) {
return;
}
- mSlotChangeHandler.onSlotsStatusChange(context);
+ mSlotChangeHandler.onSlotsStatusChange(context.getApplicationContext());
}
+ ThreadUtils.postOnMainThread(pendingResult::finish);
});
}