[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>
|
||||
|
||||
<!-- 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] -->
|
||||
<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] -->
|
||||
@@ -11982,9 +12006,31 @@
|
||||
<!-- 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>
|
||||
<!-- 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] -->
|
||||
<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 -->
|
||||
<!-- 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.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.UiccSlotInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SidecarFragment;
|
||||
import com.android.settings.network.EnableMultiSimSidecar;
|
||||
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.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */
|
||||
public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity
|
||||
@@ -41,6 +48,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
private static final String ARG_enable = "enable";
|
||||
// Dialog tags
|
||||
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.
|
||||
@@ -58,8 +70,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
|
||||
private SubscriptionInfo mSubInfo;
|
||||
private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar;
|
||||
private AlertDialog mToggleSimConfirmDialog;
|
||||
private SwitchToRemovableSlotSidecar mSwitchToRemovableSlotSidecar;
|
||||
private EnableMultiSimSidecar mEnableMultiSimSidecar;
|
||||
private boolean mEnable;
|
||||
private boolean mIsEsimOperation;
|
||||
private TelephonyManager mTelMgr;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -67,6 +82,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
|
||||
Intent intent = getIntent();
|
||||
int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
mTelMgr = getSystemService(TelephonyManager.class);
|
||||
|
||||
UserManager userManager = getSystemService(UserManager.class);
|
||||
if (!userManager.isAdminUser()) {
|
||||
@@ -82,13 +98,16 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
}
|
||||
|
||||
mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId);
|
||||
mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded();
|
||||
mSwitchToEuiccSubscriptionSidecar =
|
||||
SwitchToEuiccSubscriptionSidecar.get(getFragmentManager());
|
||||
mSwitchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(getFragmentManager());
|
||||
mEnableMultiSimSidecar = EnableMultiSimSidecar.get(getFragmentManager());
|
||||
mEnable = intent.getBooleanExtra(ARG_enable, true);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
if (mEnable) {
|
||||
handleEnablingSubAction();
|
||||
showEnableSubDialog();
|
||||
} else {
|
||||
showDisableSimConfirmDialog();
|
||||
}
|
||||
@@ -99,10 +118,14 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mSwitchToEuiccSubscriptionSidecar.addListener(this);
|
||||
mSwitchToRemovableSlotSidecar.addListener(this);
|
||||
mEnableMultiSimSidecar.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
mEnableMultiSimSidecar.removeListener(this);
|
||||
mSwitchToRemovableSlotSidecar.removeListener(this);
|
||||
mSwitchToEuiccSubscriptionSidecar.removeListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
@@ -111,19 +134,25 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
public void onStateChange(SidecarFragment fragment) {
|
||||
if (fragment == mSwitchToEuiccSubscriptionSidecar) {
|
||||
handleSwitchToEuiccSubscriptionSidecarStateChange();
|
||||
} else if (fragment == mSwitchToRemovableSlotSidecar) {
|
||||
handleSwitchToRemovableSlotSidecarStateChange();
|
||||
} else if (fragment == mEnableMultiSimSidecar) {
|
||||
handleEnableMultiSimSidecarStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case DIALOG_TAG_DISABLE_SIM_CONFIRMATION:
|
||||
if (mSubInfo.isEmbedded()) {
|
||||
if (mIsEsimOperation) {
|
||||
Log.i(TAG, "Disabling the eSIM profile.");
|
||||
showProgressDialog(
|
||||
getString(R.string.privileged_action_disable_sub_dialog_progress));
|
||||
@@ -132,6 +161,51 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
return;
|
||||
}
|
||||
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;
|
||||
default:
|
||||
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. */
|
||||
private void handleEnablingSubAction() {
|
||||
Log.i(TAG, "handleEnableSub");
|
||||
// TODO(b/160819390): Implement enabling eSIM/pSIM profile.
|
||||
private void showEnableSubDialog() {
|
||||
Log.i(TAG, "Handle subscription enabling.");
|
||||
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. */
|
||||
@@ -191,10 +360,116 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
|
||||
getString(R.string.cancel));
|
||||
}
|
||||
|
||||
/* Dismisses the SIM toggling confirmation dialog. */
|
||||
private void dismissToggleSimConfirmDialog() {
|
||||
if (mToggleSimConfirmDialog != null) {
|
||||
mToggleSimConfirmDialog.dismiss();
|
||||
private void showEnableSimConfirmDialog() {
|
||||
List<SubscriptionInfo> activeSubs =
|
||||
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
|
||||
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