Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e59d545 authored by Jiashen Wang's avatar Jiashen Wang Committed by Automerger Merge Worker
Browse files

Merge "[SIM Dialog Migration] Migrate SIM enable dialog from LPA to Settings" am: 491d2146

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/12952249

Change-Id: I422c3b64b0439a9f6ad1ad8b0d9653d4032d0880
parents cae272b2 491d2146
Loading
Loading
Loading
Loading
+47 −1
Original line number Diff line number Diff line
@@ -11981,6 +11981,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&#8230;</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] -->
@@ -11988,9 +12012,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="...">&#8230;</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 −0
Original line number Diff line number Diff line
/*
 * 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));
                        });
    }
}
+62 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
Loading