[SIM Dialog Migration] Migrate SIM enable dialog from LPA to Settings
Migrates SIM enabling and DSDS dialog from LPA to Settings. Design: https://docs.google.com/document/d/1wb5_hoBkZVbkXGNWHbx4Jf61swjfxsJzkytiTzJosYo/edit?usp=sharing Bug: 160819390 Test: Manually tested eSIM profile enabling. Change-Id: I9ce690a1594adfc90b62840819237acd54418469
This commit is contained in:
@@ -11975,6 +11975,30 @@
|
|||||||
<string name="see_less">See less</string>
|
<string name="see_less">See less</string>
|
||||||
|
|
||||||
<!-- Strings for toggling subscriptions dialog activity -->
|
<!-- Strings for toggling subscriptions dialog activity -->
|
||||||
|
<!-- Title of confirmation dialog asking the user if they want to enable subscription. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_enable_sub_dialog_title">Turn on <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
|
||||||
|
<!-- Title of confirmation dialog asking the user if they want to enable subscription without the subscription name. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_enable_sub_dialog_title_without_carrier_name">Turn on SIM?</string>
|
||||||
|
<!-- Title of confirmation dialog asking the user if they want to switch subscription. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_switch_sub_dialog_title">Switch to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
|
||||||
|
<!-- Title of confirmation dialog asking the user if they want to switch to the SIM card. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_switch_psim_dialog_title">Switch to using SIM card?</string>
|
||||||
|
<!-- Body text of confirmation dialog for switching subscription that involves switching SIM slots. Indicates that only one SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_switch_sub_dialog_text">Only one SIM can be active at a time.\n\nSwitching to <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> won\u2019t cancel your <xliff:g id="from_carrier_name" example="Sprint">%2$s</xliff:g> service.</string>
|
||||||
|
<!-- Body text of confirmation dialog for switching subscription between two eSIM profiles. Indicates that only one downloaded SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_switch_sub_dialog_text_downloaded">Only one downloaded SIM can be active at a time.\n\nSwitching to <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> won\u2019t cancel your <xliff:g id="from_carrier_name" example="Sprint">%2$s</xliff:g> service.</string>
|
||||||
|
<!-- Body text of confirmation dialog for switching subscription between two eSIM profiles. Indicates that only one SIM can be active at a time. Also that switching will not cancel the user's mobile service plan. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_switch_sub_dialog_text_single_sim">Only one SIM can be active at a time.\n\nSwitching won\u2019t cancel your <xliff:g id="to_carrier_name" example="Google Fi">%1$s</xliff:g> service.</string>
|
||||||
|
<!-- Text of confirm button in the confirmation dialog asking the user if they want to switch subscription. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_switch_sub_dialog_confirm">Switch to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
|
||||||
|
<!-- Status message indicating the device is in the process of disconnecting from one mobile network and immediately connecting to another. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_enabling_sim_without_carrier_name">Connecting to network…</string>
|
||||||
|
<!-- Text of progress dialog indicating the subscription switch is in progress. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_switch_sub_dialog_progress">Switching to <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g></string>
|
||||||
|
<!-- Title of error message indicating that the device could not disconnect from one mobile network and immediately connect to another. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_enable_sim_fail_title">Can\u2019t switch carrier</string>
|
||||||
|
<!-- Body text of error message indicating the device could not disconnect from one mobile network and immediately connect to another, due to an unspecified issue. [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_enable_sim_fail_text">The carrier can\u2019t be switched due to an error.</string>
|
||||||
<!-- Title of confirmation dialog asking the user if they want to disable subscription. [CHAR_LIMIT=NONE] -->
|
<!-- Title of confirmation dialog asking the user if they want to disable subscription. [CHAR_LIMIT=NONE] -->
|
||||||
<string name="privileged_action_disable_sub_dialog_title">Turn off <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
|
<string name="privileged_action_disable_sub_dialog_title">Turn off <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>?</string>
|
||||||
<!-- Title of confirmation dialog asking the user if they want to disable subscription. [CHAR_LIMIT=NONE] -->
|
<!-- Title of confirmation dialog asking the user if they want to disable subscription. [CHAR_LIMIT=NONE] -->
|
||||||
@@ -11982,9 +12006,31 @@
|
|||||||
<!-- Disabling SIMs progress dialog message [CHAR LIMIT=NONE] -->
|
<!-- Disabling SIMs progress dialog message [CHAR LIMIT=NONE] -->
|
||||||
<string name="privileged_action_disable_sub_dialog_progress">Turning off SIM<xliff:g id="ellipsis" example="...">…</xliff:g></string>
|
<string name="privileged_action_disable_sub_dialog_progress">Turning off SIM<xliff:g id="ellipsis" example="...">…</xliff:g></string>
|
||||||
<!-- Title of error messaging indicating the device could not disable the mobile network. [CHAR LIMIT=NONE] -->
|
<!-- Title of error messaging indicating the device could not disable the mobile network. [CHAR LIMIT=NONE] -->
|
||||||
<string name="privileged_action_disable_fail_title">Can\'t disable carrier</string>
|
<string name="privileged_action_disable_fail_title">Can\u2019t disable carrier</string>
|
||||||
<!-- Body text of error message indicating the device could not disable the mobile network, due to an unknown issue. [CHAR LIMIT=NONE] -->
|
<!-- 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>
|
<string name="privileged_action_disable_fail_text">Something went wrong and your carrier could not be disabled.</string>
|
||||||
|
<!-- Title on a dialog asking the users whether they want to enable DSDS mode. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_enable_dsds_title">Use 2 SIMs?</string>
|
||||||
|
<!-- Message in a dialog indicating the user can enable DSDS mode. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_enable_dsds_text">This device can have 2 SIMs active at once. To continue using 1 SIM at a time, tap \"No thanks\".</string>
|
||||||
|
<!-- Ask the user whether to restart device. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_restart_title">Restart device?</string>
|
||||||
|
<!-- Tell the user that in order to enable DSDS mode, the phone needs to restart. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="sim_action_restart_text">To get started, restart your device. Then you can add another SIM.</string>
|
||||||
|
<!-- Button on a dialog to confirm SIM operations. [CHAR LIMIT=30] -->
|
||||||
|
<string name="sim_action_continue">Continue</string>
|
||||||
|
<!-- User confirms reboot the phone. [CHAR LIMIT=30] -->
|
||||||
|
<string name="sim_action_reboot">Restart</string>
|
||||||
|
<!-- Button on a dialog to reject SIM operations. [CHAR LIMIT=30] -->
|
||||||
|
<string name="sim_action_no_thanks">No thanks</string>
|
||||||
|
<!-- Button which will disconnect the user from one mobile network and immediately connect to another. [CHAR LIMIT=30] -->
|
||||||
|
<string name="sim_switch_button">Switch</string>
|
||||||
|
<!-- Title of DSDS activation failure dialog [CHAR LIMIT=40] -->
|
||||||
|
<string name="dsds_activation_failure_title">Can\u2019t activate SIM</string>
|
||||||
|
<!-- Body text of DSDS activation failure dialog. Users could reinsert the SIM card or reboot to recover. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="dsds_activation_failure_body_msg1">Remove the SIM and insert it again. If the problem continues, restart your device.</string>
|
||||||
|
<!-- Body text of DSDS activation failure dialog. Users could toggle the selected SIM again or reboot to recover. [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="dsds_activation_failure_body_msg2">Try turning on the SIM again. If the problem continues, restart your device.</string>
|
||||||
|
|
||||||
<!-- Strings for deleting eUICC subscriptions dialog activity -->
|
<!-- 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] -->
|
<!-- Title on confirmation dialog asking the user if they want to erase the downloaded SIM from the device. [CHAR_LIMIT=NONE] -->
|
||||||
|
66
src/com/android/settings/AsyncTaskSidecar.java
Normal file
66
src/com/android/settings/AsyncTaskSidecar.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
/** A {@link SidecarFragment} which uses an {@link AsyncTask} to perform background work. */
|
||||||
|
public abstract class AsyncTaskSidecar<Param, Result> extends SidecarFragment {
|
||||||
|
|
||||||
|
private Future<Result> mAsyncTask;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
if (mAsyncTask != null) {
|
||||||
|
mAsyncTask.cancel(true /* mayInterruptIfRunning */);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the background task.
|
||||||
|
*
|
||||||
|
* @param param parameters passed in from {@link #run}
|
||||||
|
*/
|
||||||
|
protected abstract Result doInBackground(@Nullable Param param);
|
||||||
|
|
||||||
|
/** Handles the background task's result. */
|
||||||
|
protected void onPostExecute(Result result) {}
|
||||||
|
|
||||||
|
/** Runs the sidecar and sets the state to RUNNING. */
|
||||||
|
public void run(@Nullable final Param param) {
|
||||||
|
setState(State.RUNNING, Substate.UNUSED);
|
||||||
|
|
||||||
|
if (mAsyncTask != null) {
|
||||||
|
mAsyncTask.cancel(true /* mayInterruptIfRunning */);
|
||||||
|
}
|
||||||
|
|
||||||
|
mAsyncTask =
|
||||||
|
ThreadUtils.postOnBackgroundThread(
|
||||||
|
() -> {
|
||||||
|
Result result = doInBackground(param);
|
||||||
|
ThreadUtils.postOnMainThread(() -> onPostExecute(result));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.telephony.CarrierConfigManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
/** A receiver listens to the carrier config changes. */
|
||||||
|
public class CarrierConfigChangedReceiver extends BroadcastReceiver {
|
||||||
|
private static final String TAG = "CarrierConfigChangedReceiver";
|
||||||
|
private static final String ACTION_CARRIER_CONFIG_CHANGED =
|
||||||
|
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
|
||||||
|
|
||||||
|
private final CountDownLatch mLatch;
|
||||||
|
|
||||||
|
public CarrierConfigChangedReceiver(CountDownLatch latch) {
|
||||||
|
mLatch = latch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerOn(Context context) {
|
||||||
|
context.registerReceiver(this, new IntentFilter(ACTION_CARRIER_CONFIG_CHANGED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (isInitialStickyBroadcast()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
|
||||||
|
checkSubscriptionIndex(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkSubscriptionIndex(Intent intent) {
|
||||||
|
if (intent.hasExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX)) {
|
||||||
|
int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, -1);
|
||||||
|
Log.i(TAG, "subId from config changed: " + subId);
|
||||||
|
mLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
190
src/com/android/settings/network/EnableMultiSimSidecar.java
Normal file
190
src/com/android/settings/network/EnableMultiSimSidecar.java
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import android.app.FragmentManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.telephony.CarrierConfigManager;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
import android.telephony.UiccSlotInfo;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.settings.AsyncTaskSidecar;
|
||||||
|
import com.android.settings.SidecarFragment;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code EnableMultiSimSidecar} enables multi SIM on the device. It should only be called for
|
||||||
|
* Android R+. After {@code run} is called, it sets the configuration on modem side to enable
|
||||||
|
* multiple SIMs. Once the configuration is set successfully, it will listen to UICC card changes
|
||||||
|
* until {@code TelMan.EXTRA_ACTIVE_SIM_SUPPORTED_COUNT} matches {@code mNumOfActiveSim} or timeout.
|
||||||
|
*/
|
||||||
|
public class EnableMultiSimSidecar extends AsyncTaskSidecar<Void, Boolean> {
|
||||||
|
|
||||||
|
// Tags
|
||||||
|
private static final String TAG = "EnableMultiSimSidecar";
|
||||||
|
|
||||||
|
// TODO(b/171846124): Pass timeout value from LPA to Settings
|
||||||
|
private static final long ENABLE_MULTI_SIM_TIMEOUT_MILLS = 40 * 1000L;
|
||||||
|
|
||||||
|
public static EnableMultiSimSidecar get(FragmentManager fm) {
|
||||||
|
return SidecarFragment.get(fm, TAG, EnableMultiSimSidecar.class, null /* args */);
|
||||||
|
}
|
||||||
|
|
||||||
|
final CountDownLatch mSimCardStateChangedLatch = new CountDownLatch(1);
|
||||||
|
private TelephonyManager mTelephonyManager;
|
||||||
|
private int mNumOfActiveSim = 0;
|
||||||
|
|
||||||
|
private final BroadcastReceiver mCarrierConfigChangeReceiver =
|
||||||
|
new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
int readySimsCount = getReadySimsCount();
|
||||||
|
int activeSlotsCount = getActiveSlotsCount();
|
||||||
|
// If the number of ready SIM count and active slots equal to the number of SIMs
|
||||||
|
// need to be activated, the device is successfully switched to multiple active
|
||||||
|
// SIM mode.
|
||||||
|
if (readySimsCount == mNumOfActiveSim && activeSlotsCount == mNumOfActiveSim) {
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
String.format("%d slots are active and ready.", mNumOfActiveSim));
|
||||||
|
mSimCardStateChangedLatch.countDown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
String.format(
|
||||||
|
"%d slots are active and %d SIMs are ready. Keep waiting until"
|
||||||
|
+ " timeout.",
|
||||||
|
activeSlotsCount, readySimsCount));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void aVoid) {
|
||||||
|
return updateMultiSimConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean isDsdsEnabled) {
|
||||||
|
if (isDsdsEnabled) {
|
||||||
|
setState(State.SUCCESS, Substate.UNUSED);
|
||||||
|
} else {
|
||||||
|
setState(State.ERROR, Substate.UNUSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(int numberOfSimToActivate) {
|
||||||
|
mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
|
||||||
|
mNumOfActiveSim = numberOfSimToActivate;
|
||||||
|
|
||||||
|
if (mNumOfActiveSim > mTelephonyManager.getSupportedModemCount()) {
|
||||||
|
Log.e(TAG, "Requested number of active SIM is greater than supported modem count.");
|
||||||
|
setState(State.ERROR, Substate.UNUSED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mTelephonyManager.doesSwitchMultiSimConfigTriggerReboot()) {
|
||||||
|
Log.e(TAG, "The device does not support reboot free DSDS.");
|
||||||
|
setState(State.ERROR, Substate.UNUSED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.run(null /* param */);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method registers a ACTION_SIM_CARD_STATE_CHANGED broadcast receiver and wait for slot
|
||||||
|
// changes. If multi SIMs have been successfully enabled, it returns true. Otherwise, returns
|
||||||
|
// false.
|
||||||
|
private boolean updateMultiSimConfig() {
|
||||||
|
try {
|
||||||
|
getContext()
|
||||||
|
.registerReceiver(
|
||||||
|
mCarrierConfigChangeReceiver,
|
||||||
|
new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
|
||||||
|
mTelephonyManager.switchMultiSimConfig(mNumOfActiveSim);
|
||||||
|
if (mSimCardStateChangedLatch.await(
|
||||||
|
ENABLE_MULTI_SIM_TIMEOUT_MILLS, TimeUnit.MILLISECONDS)) {
|
||||||
|
Log.i(TAG, "Multi SIM were successfully enabled.");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Timeout for waiting SIM status.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(TAG, "Failed to enable multiple SIM due to InterruptedException", e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
getContext().unregisterReceiver(mCarrierConfigChangeReceiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns how many SIMs have SIM ready state, not ready state, or removable slot with absent
|
||||||
|
// SIM state.
|
||||||
|
private int getReadySimsCount() {
|
||||||
|
int readyCardsCount = 0;
|
||||||
|
int activeSlotCount = mTelephonyManager.getActiveModemCount();
|
||||||
|
Set<Integer> activeRemovableLogicalSlots = getActiveRemovableLogicalSlotIds();
|
||||||
|
for (int logicalSlotId = 0; logicalSlotId < activeSlotCount; logicalSlotId++) {
|
||||||
|
int simState = mTelephonyManager.getSimState(logicalSlotId);
|
||||||
|
if (simState == TelephonyManager.SIM_STATE_READY
|
||||||
|
|| simState == TelephonyManager.SIM_STATE_NOT_READY
|
||||||
|
|| simState == TelephonyManager.SIM_STATE_LOADED
|
||||||
|
|| (simState == TelephonyManager.SIM_STATE_ABSENT
|
||||||
|
&& activeRemovableLogicalSlots.contains(logicalSlotId))) {
|
||||||
|
readyCardsCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return readyCardsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get active slots count from {@code TelephonyManager#getUiccSlotsInfo}.
|
||||||
|
private int getActiveSlotsCount() {
|
||||||
|
UiccSlotInfo[] slotsInfo = mTelephonyManager.getUiccSlotsInfo();
|
||||||
|
if (slotsInfo == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int activeSlots = 0;
|
||||||
|
for (UiccSlotInfo slotInfo : slotsInfo) {
|
||||||
|
if (slotInfo != null && slotInfo.getIsActive()) {
|
||||||
|
activeSlots++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activeSlots;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a list of active removable logical slot ids. */
|
||||||
|
public Set<Integer> getActiveRemovableLogicalSlotIds() {
|
||||||
|
UiccSlotInfo[] infos = mTelephonyManager.getUiccSlotsInfo();
|
||||||
|
if (infos == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
Set<Integer> activeRemovableLogicalSlotIds = new ArraySet<>();
|
||||||
|
for (UiccSlotInfo info : infos) {
|
||||||
|
if (info != null && info.getIsActive() && info.isRemovable()) {
|
||||||
|
activeRemovableLogicalSlotIds.add(info.getLogicalSlotIdx());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activeRemovableLogicalSlotIds;
|
||||||
|
}
|
||||||
|
}
|
115
src/com/android/settings/network/SwitchSlotSidecar.java
Normal file
115
src/com/android/settings/network/SwitchSlotSidecar.java
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
import android.app.FragmentManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.settings.AsyncTaskSidecar;
|
||||||
|
import com.android.settings.SidecarFragment;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/** {@link SidecarFragment} to switch SIM slot. */
|
||||||
|
public class SwitchSlotSidecar
|
||||||
|
extends AsyncTaskSidecar<SwitchSlotSidecar.Param, SwitchSlotSidecar.Result> {
|
||||||
|
private static final String TAG = "SwitchSlotSidecar";
|
||||||
|
|
||||||
|
/** Commands */
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({
|
||||||
|
Command.SWITCH_TO_REMOVABLE_SIM,
|
||||||
|
})
|
||||||
|
private @interface Command {
|
||||||
|
int SWITCH_TO_REMOVABLE_SIM = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Param {
|
||||||
|
@Command int command;
|
||||||
|
int slotId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Result {
|
||||||
|
Exception exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a SwitchSlotSidecar sidecar instance. */
|
||||||
|
public static SwitchSlotSidecar get(FragmentManager fm) {
|
||||||
|
return SidecarFragment.get(fm, TAG, SwitchSlotSidecar.class, null /* args */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable private Exception mException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Starts switching to the removable slot. */
|
||||||
|
public void runSwitchToRemovableSlot(int id) {
|
||||||
|
Param param = new Param();
|
||||||
|
param.command = Command.SWITCH_TO_REMOVABLE_SIM;
|
||||||
|
param.slotId = id;
|
||||||
|
super.run(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exception thrown during the execution of a command. Will be null in any state
|
||||||
|
* other than {@link State#SUCCESS}, and may be null in that state if there was not an error.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Exception getException() {
|
||||||
|
return mException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Result doInBackground(@Nullable Param param) {
|
||||||
|
Result result = new Result();
|
||||||
|
if (param == null) {
|
||||||
|
result.exception = new UiccSlotsException("Null param");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch (param.command) {
|
||||||
|
case Command.SWITCH_TO_REMOVABLE_SIM:
|
||||||
|
UiccSlotUtil.switchToRemovableSlot(param.slotId, getContext());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "Wrong command.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (UiccSlotsException e) {
|
||||||
|
result.exception = e;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Result result) {
|
||||||
|
if (result.exception == null) {
|
||||||
|
setState(State.SUCCESS, Substate.UNUSED);
|
||||||
|
} else {
|
||||||
|
mException = result.exception;
|
||||||
|
setState(State.ERROR, Substate.UNUSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import android.app.FragmentManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.telephony.SubscriptionInfo;
|
||||||
|
import android.telephony.SubscriptionManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.settings.SidecarFragment;
|
||||||
|
import com.android.settings.network.telephony.EuiccOperationSidecar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sidecar is responsible for switching to the removable slot. It disables the active eSIM
|
||||||
|
* profile before switching if there is one.
|
||||||
|
*/
|
||||||
|
public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar
|
||||||
|
implements SidecarFragment.Listener {
|
||||||
|
|
||||||
|
private static final String TAG = "DisableSubscriptionAndSwitchSlotSidecar";
|
||||||
|
private static final String ACTION_DISABLE_SUBSCRIPTION_AND_SWITCH_SLOT =
|
||||||
|
"disable_subscription_and_switch_slot_sidecar";
|
||||||
|
|
||||||
|
// Stateless members.
|
||||||
|
private SwitchToEuiccSubscriptionSidecar mSwitchToSubscriptionSidecar;
|
||||||
|
private SwitchSlotSidecar mSwitchSlotSidecar;
|
||||||
|
private int mPhysicalSlotId;
|
||||||
|
|
||||||
|
/** Returns a SwitchToRemovableSlotSidecar sidecar instance. */
|
||||||
|
public static SwitchToRemovableSlotSidecar get(FragmentManager fm) {
|
||||||
|
return SidecarFragment.get(fm, TAG, SwitchToRemovableSlotSidecar.class, null /* args */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
mSwitchToSubscriptionSidecar =
|
||||||
|
SwitchToEuiccSubscriptionSidecar.get(getChildFragmentManager());
|
||||||
|
mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
mSwitchToSubscriptionSidecar.addListener(this);
|
||||||
|
mSwitchSlotSidecar.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
mSwitchToSubscriptionSidecar.removeListener(this);
|
||||||
|
mSwitchSlotSidecar.removeListener(this);
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getReceiverAction() {
|
||||||
|
return ACTION_DISABLE_SUBSCRIPTION_AND_SWITCH_SLOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStateChange(SidecarFragment fragment) {
|
||||||
|
if (fragment == mSwitchToSubscriptionSidecar) {
|
||||||
|
onSwitchToSubscriptionSidecarStateChange();
|
||||||
|
} else if (fragment == mSwitchSlotSidecar) {
|
||||||
|
onSwitchSlotSidecarStateChange();
|
||||||
|
} else {
|
||||||
|
Log.wtf(TAG, "Received state change from a sidecar not expected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts switching to the removable slot. It disables the active eSIM profile before switching
|
||||||
|
* if there is one.
|
||||||
|
*
|
||||||
|
* @param physicalSlotId removable physical SIM slot ID.
|
||||||
|
*/
|
||||||
|
public void run(int physicalSlotId) {
|
||||||
|
mPhysicalSlotId = physicalSlotId;
|
||||||
|
SubscriptionManager subscriptionManager =
|
||||||
|
getContext().getSystemService(SubscriptionManager.class);
|
||||||
|
if (SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
|
||||||
|
.anyMatch(SubscriptionInfo::isEmbedded)) {
|
||||||
|
Log.i(TAG, "There is an active eSIM profile. Disable the profile first.");
|
||||||
|
// Use INVALID_SUBSCRIPTION_ID to disable the only active profile.
|
||||||
|
mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "There is no active eSIM profiles. Start to switch to removable slot.");
|
||||||
|
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSwitchToSubscriptionSidecarStateChange() {
|
||||||
|
switch (mSwitchToSubscriptionSidecar.getState()) {
|
||||||
|
case State.SUCCESS:
|
||||||
|
mSwitchToSubscriptionSidecar.reset();
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"Successfully disabled eSIM profile. Start to switch to Removable slot.");
|
||||||
|
mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId);
|
||||||
|
break;
|
||||||
|
case State.ERROR:
|
||||||
|
mSwitchToSubscriptionSidecar.reset();
|
||||||
|
Log.i(TAG, "Failed to disable the active eSIM profile.");
|
||||||
|
setState(State.ERROR, Substate.UNUSED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSwitchSlotSidecarStateChange() {
|
||||||
|
switch (mSwitchSlotSidecar.getState()) {
|
||||||
|
case State.SUCCESS:
|
||||||
|
mSwitchSlotSidecar.reset();
|
||||||
|
Log.i(TAG, "Successfully switched to removable slot.");
|
||||||
|
setState(State.SUCCESS, Substate.UNUSED);
|
||||||
|
break;
|
||||||
|
case State.ERROR:
|
||||||
|
mSwitchSlotSidecar.reset();
|
||||||
|
Log.i(TAG, "Failed to switch to removable slot.");
|
||||||
|
setState(State.ERROR, Substate.UNUSED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
153
src/com/android/settings/network/UiccSlotUtil.java
Normal file
153
src/com/android/settings/network/UiccSlotUtil.java
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
import android.telephony.UiccSlotInfo;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class UiccSlotUtil {
|
||||||
|
|
||||||
|
private static final String TAG = "UiccSlotUtil";
|
||||||
|
|
||||||
|
// TODO(b/171846124): Pass timeout value from LPA to Settings
|
||||||
|
private static final long WAIT_AFTER_SWITCH_TIMEOUT_MILLIS = 25000;
|
||||||
|
|
||||||
|
public static final int INVALID_PHYSICAL_SLOT_ID = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode for switching to eSIM slot which decides whether there is cleanup process, e.g.
|
||||||
|
* disabling test profile, after eSIM slot is activated and whether we will wait it finished.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({
|
||||||
|
SwitchingEsimMode.NO_CLEANUP,
|
||||||
|
SwitchingEsimMode.ASYNC_CLEANUP,
|
||||||
|
SwitchingEsimMode.SYNC_CLEANUP
|
||||||
|
})
|
||||||
|
public @interface SwitchingEsimMode {
|
||||||
|
/** No cleanup process after switching to eSIM slot */
|
||||||
|
int NO_CLEANUP = 0;
|
||||||
|
/** Has cleanup process, but we will not wait it finished. */
|
||||||
|
int ASYNC_CLEANUP = 1;
|
||||||
|
/** Has cleanup process and we will wait until it's finished */
|
||||||
|
int SYNC_CLEANUP = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an immutable list of all UICC slots. If TelephonyManager#getUiccSlotsInfo returns, it
|
||||||
|
* returns an empty list instead.
|
||||||
|
*/
|
||||||
|
public static ImmutableList<UiccSlotInfo> getSlotInfos(TelephonyManager telMgr) {
|
||||||
|
UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo();
|
||||||
|
if (slotInfos == null) {
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
return ImmutableList.copyOf(slotInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches to the removable slot. It waits for SIM_STATE_LOADED after switch. If slotId is
|
||||||
|
* INVALID_PHYSICAL_SLOT_ID, the method will use the first detected inactive removable slot.
|
||||||
|
*
|
||||||
|
* @param slotId the physical removable slot id.
|
||||||
|
* @param context the application context.
|
||||||
|
* @throws UiccSlotsException if there is an error.
|
||||||
|
*/
|
||||||
|
public static synchronized void switchToRemovableSlot(int slotId, Context context)
|
||||||
|
throws UiccSlotsException {
|
||||||
|
if (ThreadUtils.isMainThread()) {
|
||||||
|
throw new IllegalThreadStateException(
|
||||||
|
"Do not call switchToRemovableSlot on the main thread.");
|
||||||
|
}
|
||||||
|
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
|
||||||
|
if (telMgr.isMultiSimEnabled()) {
|
||||||
|
// If this device supports multiple active slots, don't mess with TelephonyManager.
|
||||||
|
Log.i(TAG, "Multiple active slots supported. Not calling switchSlots.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UiccSlotInfo[] slots = telMgr.getUiccSlotsInfo();
|
||||||
|
if (slotId == INVALID_PHYSICAL_SLOT_ID) {
|
||||||
|
for (int i = 0; i < slots.length; i++) {
|
||||||
|
if (slots[i].isRemovable()
|
||||||
|
&& !slots[i].getIsActive()
|
||||||
|
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR
|
||||||
|
&& slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
|
||||||
|
performSwitchToRemovableSlot(i, context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (slotId >= slots.length || !slots[slotId].isRemovable()) {
|
||||||
|
throw new UiccSlotsException("The given slotId is not a removable slot: " + slotId);
|
||||||
|
}
|
||||||
|
if (!slots[slotId].getIsActive()) {
|
||||||
|
performSwitchToRemovableSlot(slotId, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void performSwitchToRemovableSlot(int slotId, Context context)
|
||||||
|
throws UiccSlotsException {
|
||||||
|
CarrierConfigChangedReceiver receiver = null;
|
||||||
|
try {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
receiver = new CarrierConfigChangedReceiver(latch);
|
||||||
|
receiver.registerOn(context);
|
||||||
|
switchSlots(context, slotId);
|
||||||
|
latch.await(WAIT_AFTER_SWITCH_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
Log.e(TAG, "Failed switching to physical slot.", e);
|
||||||
|
} finally {
|
||||||
|
if (receiver != null) {
|
||||||
|
context.unregisterReceiver(receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the logical slot to physical slot mapping. OEM should override this to provide
|
||||||
|
* device-specific implementation if the device supports switching slots.
|
||||||
|
*
|
||||||
|
* @param context the application context.
|
||||||
|
* @param physicalSlots List of physical slot ids in the order of logical slots.
|
||||||
|
*/
|
||||||
|
private static void switchSlots(Context context, int... physicalSlots)
|
||||||
|
throws UiccSlotsException {
|
||||||
|
TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
|
||||||
|
if (telMgr.isMultiSimEnabled()) {
|
||||||
|
// If this device supports multiple active slots, don't mess with TelephonyManager.
|
||||||
|
Log.i(TAG, "Multiple active slots supported. Not calling switchSlots.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!telMgr.switchSlots(physicalSlots)) {
|
||||||
|
throw new UiccSlotsException("Failed to switch slots");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
src/com/android/settings/network/UiccSlotsException.java
Normal file
35
src/com/android/settings/network/UiccSlotsException.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/** The exception that is thrown when an error happens in a call to {@link UiccSlotUtil}. */
|
||||||
|
public class UiccSlotsException extends Exception {
|
||||||
|
|
||||||
|
public UiccSlotsException() {}
|
||||||
|
|
||||||
|
public UiccSlotsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiccSlotsException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiccSlotsException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@@ -22,15 +22,22 @@ import android.os.Bundle;
|
|||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.telephony.SubscriptionInfo;
|
import android.telephony.SubscriptionInfo;
|
||||||
import android.telephony.SubscriptionManager;
|
import android.telephony.SubscriptionManager;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
import android.telephony.UiccSlotInfo;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SidecarFragment;
|
import com.android.settings.SidecarFragment;
|
||||||
|
import com.android.settings.network.EnableMultiSimSidecar;
|
||||||
import com.android.settings.network.SubscriptionUtil;
|
import com.android.settings.network.SubscriptionUtil;
|
||||||
import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
|
import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
|
||||||
|
import com.android.settings.network.SwitchToRemovableSlotSidecar;
|
||||||
|
import com.android.settings.network.UiccSlotUtil;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */
|
/** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */
|
||||||
public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity
|
public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity
|
||||||
@@ -41,6 +48,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
private static final String ARG_enable = "enable";
|
private static final String ARG_enable = "enable";
|
||||||
// Dialog tags
|
// Dialog tags
|
||||||
private static final int DIALOG_TAG_DISABLE_SIM_CONFIRMATION = 1;
|
private static final int DIALOG_TAG_DISABLE_SIM_CONFIRMATION = 1;
|
||||||
|
private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION = 2;
|
||||||
|
private static final int DIALOG_TAG_ENABLE_DSDS_CONFIRMATION = 3;
|
||||||
|
private static final int DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION = 4;
|
||||||
|
// Number of SIMs for DSDS
|
||||||
|
private static final int NUM_OF_SIMS_FOR_DSDS = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an intent of ToggleSubscriptionDialogActivity.
|
* Returns an intent of ToggleSubscriptionDialogActivity.
|
||||||
@@ -58,8 +70,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
|
|
||||||
private SubscriptionInfo mSubInfo;
|
private SubscriptionInfo mSubInfo;
|
||||||
private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar;
|
private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar;
|
||||||
private AlertDialog mToggleSimConfirmDialog;
|
private SwitchToRemovableSlotSidecar mSwitchToRemovableSlotSidecar;
|
||||||
|
private EnableMultiSimSidecar mEnableMultiSimSidecar;
|
||||||
private boolean mEnable;
|
private boolean mEnable;
|
||||||
|
private boolean mIsEsimOperation;
|
||||||
|
private TelephonyManager mTelMgr;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -67,6 +82,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||||
|
mTelMgr = getSystemService(TelephonyManager.class);
|
||||||
|
|
||||||
UserManager userManager = getSystemService(UserManager.class);
|
UserManager userManager = getSystemService(UserManager.class);
|
||||||
if (!userManager.isAdminUser()) {
|
if (!userManager.isAdminUser()) {
|
||||||
@@ -82,13 +98,16 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId);
|
mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId);
|
||||||
|
mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded();
|
||||||
mSwitchToEuiccSubscriptionSidecar =
|
mSwitchToEuiccSubscriptionSidecar =
|
||||||
SwitchToEuiccSubscriptionSidecar.get(getFragmentManager());
|
SwitchToEuiccSubscriptionSidecar.get(getFragmentManager());
|
||||||
|
mSwitchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(getFragmentManager());
|
||||||
|
mEnableMultiSimSidecar = EnableMultiSimSidecar.get(getFragmentManager());
|
||||||
mEnable = intent.getBooleanExtra(ARG_enable, true);
|
mEnable = intent.getBooleanExtra(ARG_enable, true);
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
if (mEnable) {
|
if (mEnable) {
|
||||||
handleEnablingSubAction();
|
showEnableSubDialog();
|
||||||
} else {
|
} else {
|
||||||
showDisableSimConfirmDialog();
|
showDisableSimConfirmDialog();
|
||||||
}
|
}
|
||||||
@@ -99,10 +118,14 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
mSwitchToEuiccSubscriptionSidecar.addListener(this);
|
mSwitchToEuiccSubscriptionSidecar.addListener(this);
|
||||||
|
mSwitchToRemovableSlotSidecar.addListener(this);
|
||||||
|
mEnableMultiSimSidecar.addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
|
mEnableMultiSimSidecar.removeListener(this);
|
||||||
|
mSwitchToRemovableSlotSidecar.removeListener(this);
|
||||||
mSwitchToEuiccSubscriptionSidecar.removeListener(this);
|
mSwitchToEuiccSubscriptionSidecar.removeListener(this);
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
@@ -111,19 +134,25 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
public void onStateChange(SidecarFragment fragment) {
|
public void onStateChange(SidecarFragment fragment) {
|
||||||
if (fragment == mSwitchToEuiccSubscriptionSidecar) {
|
if (fragment == mSwitchToEuiccSubscriptionSidecar) {
|
||||||
handleSwitchToEuiccSubscriptionSidecarStateChange();
|
handleSwitchToEuiccSubscriptionSidecarStateChange();
|
||||||
|
} else if (fragment == mSwitchToRemovableSlotSidecar) {
|
||||||
|
handleSwitchToRemovableSlotSidecarStateChange();
|
||||||
|
} else if (fragment == mEnableMultiSimSidecar) {
|
||||||
|
handleEnableMultiSimSidecarStateChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfirm(int tag, boolean confirmed) {
|
public void onConfirm(int tag, boolean confirmed) {
|
||||||
if (!confirmed) {
|
if (!confirmed
|
||||||
|
&& tag != DIALOG_TAG_ENABLE_DSDS_CONFIRMATION
|
||||||
|
&& tag != DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION) {
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case DIALOG_TAG_DISABLE_SIM_CONFIRMATION:
|
case DIALOG_TAG_DISABLE_SIM_CONFIRMATION:
|
||||||
if (mSubInfo.isEmbedded()) {
|
if (mIsEsimOperation) {
|
||||||
Log.i(TAG, "Disabling the eSIM profile.");
|
Log.i(TAG, "Disabling the eSIM profile.");
|
||||||
showProgressDialog(
|
showProgressDialog(
|
||||||
getString(R.string.privileged_action_disable_sub_dialog_progress));
|
getString(R.string.privileged_action_disable_sub_dialog_progress));
|
||||||
@@ -132,6 +161,51 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.i(TAG, "Disabling the pSIM profile.");
|
Log.i(TAG, "Disabling the pSIM profile.");
|
||||||
|
handleTogglePsimAction();
|
||||||
|
break;
|
||||||
|
case DIALOG_TAG_ENABLE_DSDS_CONFIRMATION:
|
||||||
|
if (!confirmed) {
|
||||||
|
Log.i(TAG, "User cancel the dialog to enable DSDS.");
|
||||||
|
showEnableSimConfirmDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mTelMgr.doesSwitchMultiSimConfigTriggerReboot()) {
|
||||||
|
Log.i(TAG, "Device does not support reboot free DSDS.");
|
||||||
|
showRebootConfirmDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"Enabling DSDS without rebooting. "
|
||||||
|
+ getString(R.string.sim_action_enabling_sim_without_carrier_name));
|
||||||
|
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:
|
||||||
|
if (!confirmed) {
|
||||||
|
Log.i(TAG, "User cancel the dialog to reboot to enable DSDS.");
|
||||||
|
showEnableSimConfirmDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.i(TAG, "User confirmed reboot to enable DSDS.");
|
||||||
|
mTelMgr.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS);
|
||||||
|
// TODO(b/170507290): Store a bit in preferences for displaying the notification
|
||||||
|
// after the reboot.
|
||||||
|
break;
|
||||||
|
case DIALOG_TAG_ENABLE_SIM_CONFIRMATION:
|
||||||
|
Log.i(TAG, "User confirmed to enable the subscription.");
|
||||||
|
if (mIsEsimOperation) {
|
||||||
|
showProgressDialog(
|
||||||
|
getString(
|
||||||
|
R.string.sim_action_switch_sub_dialog_progress,
|
||||||
|
mSubInfo.getDisplayName()));
|
||||||
|
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showProgressDialog(
|
||||||
|
getString(R.string.sim_action_enabling_sim_without_carrier_name));
|
||||||
|
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag);
|
Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag);
|
||||||
@@ -165,10 +239,105 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleSwitchToRemovableSlotSidecarStateChange() {
|
||||||
|
switch (mSwitchToRemovableSlotSidecar.getState()) {
|
||||||
|
case SidecarFragment.State.SUCCESS:
|
||||||
|
Log.i(TAG, "Successfully switched to removable slot.");
|
||||||
|
mSwitchToRemovableSlotSidecar.reset();
|
||||||
|
handleTogglePsimAction();
|
||||||
|
dismissProgressDialog();
|
||||||
|
finish();
|
||||||
|
break;
|
||||||
|
case SidecarFragment.State.ERROR:
|
||||||
|
Log.e(TAG, "Failed switching to removable slot.");
|
||||||
|
mSwitchToRemovableSlotSidecar.reset();
|
||||||
|
dismissProgressDialog();
|
||||||
|
showErrorDialog(
|
||||||
|
getString(R.string.sim_action_enable_sim_fail_title),
|
||||||
|
getString(R.string.sim_action_enable_sim_fail_text));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEnableMultiSimSidecarStateChange() {
|
||||||
|
switch (mEnableMultiSimSidecar.getState()) {
|
||||||
|
case SidecarFragment.State.SUCCESS:
|
||||||
|
mEnableMultiSimSidecar.reset();
|
||||||
|
Log.i(TAG, "Successfully switched to DSDS without reboot.");
|
||||||
|
handleEnableSubscriptionAfterEnablingDsds();
|
||||||
|
break;
|
||||||
|
case SidecarFragment.State.ERROR:
|
||||||
|
mEnableMultiSimSidecar.reset();
|
||||||
|
Log.i(TAG, "Failed to switch to DSDS without rebooting.");
|
||||||
|
ProgressDialogFragment.dismiss(getFragmentManager());
|
||||||
|
showErrorDialog(
|
||||||
|
getString(R.string.dsds_activation_failure_title),
|
||||||
|
getString(R.string.dsds_activation_failure_body_msg2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEnableSubscriptionAfterEnablingDsds() {
|
||||||
|
if (mIsEsimOperation) {
|
||||||
|
Log.i(TAG, "DSDS enabled, start to enable profile: " + mSubInfo.getSubscriptionId());
|
||||||
|
// For eSIM operations, we simply switch to the selected eSIM profile.
|
||||||
|
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "DSDS enabled, start to enable pSIM profile.");
|
||||||
|
handleTogglePsimAction();
|
||||||
|
ProgressDialogFragment.dismiss(getFragmentManager());
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTogglePsimAction() {
|
||||||
|
if (mSubscriptionManager.canDisablePhysicalSubscription() && mSubInfo != null) {
|
||||||
|
mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable);
|
||||||
|
} else {
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"The device does not support toggling pSIM. It is enough to just "
|
||||||
|
+ "enable the removable slot.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Handles the enabling SIM action. */
|
/* Handles the enabling SIM action. */
|
||||||
private void handleEnablingSubAction() {
|
private void showEnableSubDialog() {
|
||||||
Log.i(TAG, "handleEnableSub");
|
Log.i(TAG, "Handle subscription enabling.");
|
||||||
// TODO(b/160819390): Implement enabling eSIM/pSIM profile.
|
if (isDsdsConditionSatisfied()) {
|
||||||
|
showEnableDsdsConfirmDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()) {
|
||||||
|
Log.i(TAG, "Toggle on pSIM, no dialog displayed.");
|
||||||
|
handleTogglePsimAction();
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showEnableSimConfirmDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Displays the SIM toggling confirmation dialog. */
|
/* Displays the SIM toggling confirmation dialog. */
|
||||||
@@ -191,10 +360,116 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
|||||||
getString(R.string.cancel));
|
getString(R.string.cancel));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dismisses the SIM toggling confirmation dialog. */
|
private void showEnableSimConfirmDialog() {
|
||||||
private void dismissToggleSimConfirmDialog() {
|
List<SubscriptionInfo> activeSubs =
|
||||||
if (mToggleSimConfirmDialog != null) {
|
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
|
||||||
mToggleSimConfirmDialog.dismiss();
|
SubscriptionInfo activeSub = activeSubs.isEmpty() ? null : activeSubs.get(0);
|
||||||
|
if (activeSub == null) {
|
||||||
|
Log.i(TAG, "No active subscriptions available.");
|
||||||
|
showNonSwitchSimConfirmDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Found active subscription.");
|
||||||
|
boolean isBetweenEsim = mIsEsimOperation && activeSub.isEmbedded();
|
||||||
|
if (mTelMgr.isMultiSimEnabled() && !isBetweenEsim) {
|
||||||
|
showNonSwitchSimConfirmDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ConfirmDialogFragment.show(
|
||||||
|
this,
|
||||||
|
ConfirmDialogFragment.OnConfirmListener.class,
|
||||||
|
DIALOG_TAG_ENABLE_SIM_CONFIRMATION,
|
||||||
|
getSwitchSubscriptionTitle(),
|
||||||
|
getSwitchDialogBodyMsg(activeSub, isBetweenEsim),
|
||||||
|
getSwitchDialogPosBtnText(),
|
||||||
|
getString(android.R.string.cancel));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showNonSwitchSimConfirmDialog() {
|
||||||
|
ConfirmDialogFragment.show(
|
||||||
|
this,
|
||||||
|
ConfirmDialogFragment.OnConfirmListener.class,
|
||||||
|
DIALOG_TAG_ENABLE_SIM_CONFIRMATION,
|
||||||
|
getEnableSubscriptionTitle(),
|
||||||
|
null /* msg */,
|
||||||
|
getString(R.string.yes),
|
||||||
|
getString(android.R.string.cancel));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSwitchDialogPosBtnText() {
|
||||||
|
return mIsEsimOperation
|
||||||
|
? getString(
|
||||||
|
R.string.sim_action_switch_sub_dialog_confirm, mSubInfo.getDisplayName())
|
||||||
|
: getString(R.string.sim_switch_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEnableSubscriptionTitle() {
|
||||||
|
if (mSubInfo == null || TextUtils.isEmpty(mSubInfo.getDisplayName())) {
|
||||||
|
return getString(R.string.sim_action_enable_sub_dialog_title_without_carrier_name);
|
||||||
|
}
|
||||||
|
return getString(R.string.sim_action_enable_sub_dialog_title, mSubInfo.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSwitchSubscriptionTitle() {
|
||||||
|
if (mIsEsimOperation) {
|
||||||
|
return getString(
|
||||||
|
R.string.sim_action_switch_sub_dialog_title, mSubInfo.getDisplayName());
|
||||||
|
}
|
||||||
|
return getString(R.string.sim_action_switch_psim_dialog_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSwitchDialogBodyMsg(SubscriptionInfo activeSub, boolean betweenEsim) {
|
||||||
|
if (betweenEsim && mIsEsimOperation) {
|
||||||
|
return getString(
|
||||||
|
R.string.sim_action_switch_sub_dialog_text_downloaded,
|
||||||
|
mSubInfo.getDisplayName(),
|
||||||
|
activeSub.getDisplayName());
|
||||||
|
} else if (mIsEsimOperation) {
|
||||||
|
return getString(
|
||||||
|
R.string.sim_action_switch_sub_dialog_text,
|
||||||
|
mSubInfo.getDisplayName(),
|
||||||
|
activeSub.getDisplayName());
|
||||||
|
} else {
|
||||||
|
return getString(
|
||||||
|
R.string.sim_action_switch_sub_dialog_text_single_sim,
|
||||||
|
activeSub.getDisplayName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isDsdsConditionSatisfied() {
|
||||||
|
if (mTelMgr.isMultiSimEnabled()) {
|
||||||
|
Log.i(TAG, "DSDS is already enabled. Condition not satisfied.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (mTelMgr.isMultiSimSupported() != TelephonyManager.MULTISIM_ALLOWED) {
|
||||||
|
Log.i(TAG, "Hardware does not support DSDS.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr);
|
||||||
|
boolean isRemovableSimEnabled =
|
||||||
|
slotInfos.stream()
|
||||||
|
.anyMatch(
|
||||||
|
slot ->
|
||||||
|
slot != null
|
||||||
|
&& slot.isRemovable()
|
||||||
|
&& slot.getIsActive()
|
||||||
|
&& slot.getCardStateInfo()
|
||||||
|
== UiccSlotInfo.CARD_STATE_INFO_PRESENT);
|
||||||
|
if (mIsEsimOperation && isRemovableSimEnabled) {
|
||||||
|
Log.i(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
boolean isEsimProfileEnabled =
|
||||||
|
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream()
|
||||||
|
.anyMatch(SubscriptionInfo::isEmbedded);
|
||||||
|
if (!mIsEsimOperation && isEsimProfileEnabled) {
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"Removable SIM operation and eSIM profile is enabled. DSDS condition"
|
||||||
|
+ " satisfied.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log.i(TAG, "DSDS condition not satisfied.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user