From 6fd0716f58e6fb15688cfd98750f9d08456bd311 Mon Sep 17 00:00:00 2001 From: Jan Tomljanovic Date: Mon, 29 Nov 2021 18:05:22 +0000 Subject: [PATCH 01/12] Add SafetyCenter entry to Settings. Test: atest SettingsUnitTests Bug: 204978295 Change-Id: I9bedb58ad740b4ee76620e04675a0aefa659566a --- res/drawable/ic_settings_safety_center.xml | 29 ++++ res/values/menu_keys.xml | 1 + res/values/strings.xml | 6 + res/xml/top_level_settings.xml | 9 ++ .../safetycenter/SafetyCenterStatus.java | 36 +++++ ...SafetyCenterEntryPreferenceController.java | 60 ++++++++ .../safetycenter/SafetyCenterStatusTest.java | 67 +++++++++ ...tyCenterEntryPreferenceControllerTest.java | 134 ++++++++++++++++++ 8 files changed, 342 insertions(+) create mode 100644 res/drawable/ic_settings_safety_center.xml create mode 100644 src/com/android/settings/safetycenter/SafetyCenterStatus.java create mode 100644 src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceController.java create mode 100644 tests/unit/src/com/android/settings/safetycenter/SafetyCenterStatusTest.java create mode 100644 tests/unit/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceControllerTest.java diff --git a/res/drawable/ic_settings_safety_center.xml b/res/drawable/ic_settings_safety_center.xml new file mode 100644 index 00000000000..f43359fefd4 --- /dev/null +++ b/res/drawable/ic_settings_safety_center.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/res/values/menu_keys.xml b/res/values/menu_keys.xml index e69664a3408..2841b699c97 100755 --- a/res/values/menu_keys.xml +++ b/res/values/menu_keys.xml @@ -26,6 +26,7 @@ top_level_display top_level_wallpaper top_level_accessibility + top_level_safety_center top_level_security top_level_privacy top_level_location diff --git a/res/values/strings.xml b/res/values/strings.xml index bf9fc2a60fe..995742d7261 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -722,6 +722,12 @@ Security status Screen lock, Find My Device, app security + + + Security & privacy + + + Permissions, screen lock, app security diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml index 042ce43999a..6005581d4d9 100644 --- a/res/xml/top_level_settings.xml +++ b/res/xml/top_level_settings.xml @@ -116,6 +116,15 @@ settings:highlightableMenuKey="@string/menu_key_accessibility" settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/> + + intentCaptor = ArgumentCaptor.forClass(Intent.class); + + boolean preferenceHandled = mTopLevelSafetyCenterEntryPreferenceController + .handlePreferenceTreeClick(mPreference); + + assertThat(preferenceHandled).isTrue(); + verify(mContext).startActivity(intentCaptor.capture()); + assertThat(intentCaptor.getValue().getAction()).isEqualTo(Intent.ACTION_SAFETY_CENTER); + } + + @Test + public void handlePreferenceTreeClick_onStartActivityThrows_returnsFalse() { + doThrow(ActivityNotFoundException.class) + .when(mContext).startActivity(any(Intent.class)); + + boolean preferenceHandled = mTopLevelSafetyCenterEntryPreferenceController + .handlePreferenceTreeClick(mPreference); + + assertThat(preferenceHandled).isFalse(); + } + + @Test + public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsUnavailable() { + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED, + /* value = */ Boolean.toString(false), + /* makeDefault = */ false); + + assertThat(mTopLevelSafetyCenterEntryPreferenceController.getAvailabilityStatus()) + .isEqualTo(TopLevelSafetyCenterEntryPreferenceController.CONDITIONALLY_UNAVAILABLE); + } + + @Test + public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsAvailable() { + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + SafetyCenterStatus.SAFETY_CENTER_IS_ENABLED, + /* value = */ Boolean.toString(true), + /* makeDefault = */ false); + + assertThat(mTopLevelSafetyCenterEntryPreferenceController.getAvailabilityStatus()) + .isEqualTo(TopLevelSafetyCenterEntryPreferenceController.AVAILABLE); + } +} From 1e0e9021acbf3a332c9fa121bc45f7eb13db2018 Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Fri, 3 Dec 2021 16:03:45 +0800 Subject: [PATCH 02/12] switch SIM refactor to support MEP 1. Using new telephony API and doing the code refactor 2. To support multi esim profiles case Bug: 199902896 Test: local build pass. Change-Id: I8580022793e5c3fc14159f14b406f864353477f8 --- res/values/strings.xml | 16 +- .../settings/network/SwitchSlotSidecar.java | 26 ++- .../SwitchToEuiccSubscriptionSidecar.java | 122 ++++++++++ .../network/SwitchToRemovableSlotSidecar.java | 32 ++- .../settings/network/UiccSlotUtil.java | 216 ++++++++++++++---- .../telephony/ConfirmDialogFragment.java | 22 +- ...DeleteEuiccSubscriptionDialogActivity.java | 2 +- .../telephony/EuiccOperationSidecar.java | 40 +++- .../ToggleSubscriptionDialogActivity.java | 152 +++++++++--- .../settings/sim/DsdsDialogActivity.java | 2 +- .../SwitchToEsimConfirmDialogActivity.java | 2 +- 11 files changed, 526 insertions(+), 106 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 17a1ece85ba..49f2d27e0e0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8122,11 +8122,17 @@ Always use this for calls - Select a SIM for data + Choose SIM for mobile data Select a SIM for SMS Switching data SIM, this may take up to a minute\u2026 + + Use %1$s for mobile data? + + If you switch to %1$s, %2$s will no longer be used for mobile data. + + Use %1$s Call with @@ -12936,14 +12942,22 @@ Switch to %1$s? Switch to using SIM card? + + Use %1$s? Only one SIM can be active at a time.\n\nSwitching to %1$s won\u2019t cancel your %2$s service. Only one downloaded SIM can be active at a time.\n\nSwitching to %1$s won\u2019t cancel your %2$s service. Only one SIM can be active at a time.\n\nSwitching won\u2019t cancel your %1$s service. + + You can use 2 SIMs at a time. To use %1$s, turn off another SIM. Switch to %1$s + + Turn off %1$s + + Turning off a SIM won\u2019t cancel your service Connecting to network… diff --git a/src/com/android/settings/network/SwitchSlotSidecar.java b/src/com/android/settings/network/SwitchSlotSidecar.java index cffb23fb03f..abf8842c738 100644 --- a/src/com/android/settings/network/SwitchSlotSidecar.java +++ b/src/com/android/settings/network/SwitchSlotSidecar.java @@ -19,6 +19,7 @@ package com.android.settings.network; import android.annotation.IntDef; import android.app.FragmentManager; import android.os.Bundle; +import android.telephony.SubscriptionInfo; import android.util.Log; import com.android.settings.AsyncTaskSidecar; @@ -41,11 +42,14 @@ public class SwitchSlotSidecar }) private @interface Command { int SWITCH_TO_REMOVABLE_SIM = 0; + int SWITCH_TO_EUICC_SIM = 1; } static class Param { @Command int command; int slotId; + int port; + SubscriptionInfo removedSubInfo; } static class Result { @@ -65,13 +69,24 @@ public class SwitchSlotSidecar } /** Starts switching to the removable slot. */ - public void runSwitchToRemovableSlot(int id) { + public void runSwitchToRemovableSlot(int id, SubscriptionInfo removedSubInfo) { Param param = new Param(); param.command = Command.SWITCH_TO_REMOVABLE_SIM; param.slotId = id; + param.removedSubInfo = removedSubInfo; + param.port = 0; super.run(param); } + /** Starts switching to the removable slot. */ + public void runSwitchToEuiccSlot(int id, int port, SubscriptionInfo removedSubInfo) { + Param param = new Param(); + param.command = Command.SWITCH_TO_EUICC_SIM; + param.slotId = id; + param.removedSubInfo = removedSubInfo; + param.port = port; + 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. @@ -91,7 +106,14 @@ public class SwitchSlotSidecar try { switch (param.command) { case Command.SWITCH_TO_REMOVABLE_SIM: - UiccSlotUtil.switchToRemovableSlot(param.slotId, getContext()); + Log.i(TAG, "Start to switch to removable slot."); + UiccSlotUtil.switchToRemovableSlot(getContext(), param.slotId, + param.removedSubInfo); + break; + case Command.SWITCH_TO_EUICC_SIM: + Log.i(TAG, "Start to switch to euicc slot."); + UiccSlotUtil.switchToEuiccSlot(getContext(), param.slotId, param.port, + param.removedSubInfo); break; default: Log.e(TAG, "Wrong command."); diff --git a/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java b/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java index 6e6142f6136..d88a189f8c7 100644 --- a/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java +++ b/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java @@ -18,17 +18,30 @@ package com.android.settings.network; import android.app.FragmentManager; import android.app.PendingIntent; +import android.content.Intent; +import android.telephony.SubscriptionInfo; +import android.telephony.UiccCardInfo; +import android.telephony.UiccSlotMapping; +import android.telephony.euicc.EuiccManager; +import android.util.Log; import com.android.settings.SidecarFragment; import com.android.settings.network.telephony.EuiccOperationSidecar; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + /** A headless fragment encapsulating long-running eSIM enabling/disabling operations. */ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar { private static final String TAG = "SwitchToEuiccSubscriptionSidecar"; private static final String ACTION_SWITCH_TO_SUBSCRIPTION = "com.android.settings.network.SWITCH_TO_SUBSCRIPTION"; + private static final int ESIM_SLOT_ID = 1; private PendingIntent mCallbackIntent; + private int mSubId; + private int mPort; /** Returns a SwitchToEuiccSubscriptionSidecar sidecar instance. */ public static SwitchToEuiccSubscriptionSidecar get(FragmentManager fm) { @@ -46,10 +59,119 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar { return mCallbackIntent; } + @Override + public void onStateChange(SidecarFragment fragment) { + if (fragment == mSwitchSlotSidecar) { + onSwitchSlotSidecarStateChange(); + } else { + Log.wtf(TAG, "Received state change from a sidecar not expected."); + } + } + /** Starts calling EuiccManager#switchToSubscription to enable/disable the eSIM profile. */ + // ToDo: delete this api and refactor the related code. public void run(int subscriptionId) { setState(State.RUNNING, Substate.UNUSED); mCallbackIntent = createCallbackIntent(); mEuiccManager.switchToSubscription(subscriptionId, mCallbackIntent); } + + /** + * Starts calling EuiccManager#switchToSubscription to enable/disable the eSIM profile. + * + * @param subscriptionId the esim's subscriptionId. + * @param port the esim's portId. If user wants to inactivate esim, then user must to assign the + * the port. If user wants to activate esim, then the port can be -1. + * @param removedSubInfo if the all of slots have sims, it should remove the one of active sim. + * If the removedSubInfo is null, then use the default value. + * The default value is the esim slot and portId 0. + */ + public void run(int subscriptionId, int port, SubscriptionInfo removedSubInfo) { + setState(State.RUNNING, Substate.UNUSED); + mCallbackIntent = createCallbackIntent(); + mSubId = subscriptionId; + // To check whether the esim slot's port is active. If yes, skip setSlotMapping. If no, + // set this slot+port into setSimSlotMapping. + mPort = (port < 0) ? getTargetPortId(removedSubInfo) : port; + Log.i(TAG, "The SubId is " + mSubId + "The port is " + mPort); + + mSwitchSlotSidecar.runSwitchToEuiccSlot(getTargetSlot(), mPort, removedSubInfo); + } + + private int getTargetPortId(SubscriptionInfo removedSubInfo) { + if (!mTelephonyManager.isMultiSimEnabled() || !isMultipleEnabledProfilesSupported()) { + // In the 'SS mode' or 'DSDS+no MEP', the port is 0. + return 0; + } + + // In the 'DSDS+MEP', if the removedSubInfo is esim, then the port is + // removedSubInfo's port. + if (removedSubInfo != null && removedSubInfo.isEmbedded()) { + return removedSubInfo.getPortIndex(); + } + + // In DSDS+MEP mode, the removedSubInfo is psim or is null, it means the this esim need + // another port in the esim slot. + // To find another esim's port and value is from 0; + int port = 0; + Collection uiccSlotMappings = mTelephonyManager.getSimSlotMapping(); + for (UiccSlotMapping uiccSlotMapping : + uiccSlotMappings.stream() + .filter( + uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex() + == getTargetSlot()) + .collect(Collectors.toList())) { + if (uiccSlotMapping.getPortIndex() == port) { + port++; + } + } + return port; + } + + private int getTargetSlot() { + return ESIM_SLOT_ID; + } + + private void onSwitchSlotSidecarStateChange() { + switch (mSwitchSlotSidecar.getState()) { + case State.SUCCESS: + mSwitchSlotSidecar.reset(); + Log.i(TAG, + "Successfully SimSlotMapping. Start to enable/disable esim"); + switchToSubscription(); + break; + case State.ERROR: + mSwitchSlotSidecar.reset(); + Log.i(TAG, "Failed to set SimSlotMapping"); + setState(State.ERROR, Substate.UNUSED); + break; + } + } + + private boolean isMultipleEnabledProfilesSupported() { + List cardInfos = mTelephonyManager.getUiccCardsInfo(); + if (cardInfos == null) { + Log.w(TAG, "UICC cards info list is empty."); + return false; + } + return cardInfos.stream().anyMatch( + cardInfo -> cardInfo.isMultipleEnabledProfilesSupported()); + } + + private void switchToSubscription() { + // The SimSlotMapping is ready, then to execute activate/inactivate esim. + EuiccManager.ResultListener callback = new EuiccManager.ResultListener() { + @Override + public void onComplete(int resultCode, Intent resultIntent) { + Log.i(TAG, String.format("Result code : %d;", resultCode)); + if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { + setState(State.SUCCESS, Substate.UNUSED); + } else { + setState(State.ERROR, resultCode); + } + } + }; + mEuiccManager.switchToSubscription(mSubId, mPort, getContext().getMainExecutor(), + callback); + } } diff --git a/src/com/android/settings/network/SwitchToRemovableSlotSidecar.java b/src/com/android/settings/network/SwitchToRemovableSlotSidecar.java index 132a2fd2367..2c184c6057a 100644 --- a/src/com/android/settings/network/SwitchToRemovableSlotSidecar.java +++ b/src/com/android/settings/network/SwitchToRemovableSlotSidecar.java @@ -38,8 +38,8 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar // Stateless members. private SwitchToEuiccSubscriptionSidecar mSwitchToSubscriptionSidecar; - private SwitchSlotSidecar mSwitchSlotSidecar; private int mPhysicalSlotId; + private SubscriptionInfo mRemovedSubInfo; /** Returns a SwitchToRemovableSlotSidecar sidecar instance. */ public static SwitchToRemovableSlotSidecar get(FragmentManager fm) { @@ -51,20 +51,17 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar 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(); } @@ -90,29 +87,46 @@ public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar * * @param physicalSlotId removable physical SIM slot ID. */ + // ToDo: delete this api and refactor the related code. public void run(int physicalSlotId) { mPhysicalSlotId = physicalSlotId; SubscriptionManager subscriptionManager = getContext().getSystemService(SubscriptionManager.class); if (SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream() .anyMatch(SubscriptionInfo::isEmbedded)) { + // In SS mode, the esim is active, then inactivate the esim. 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); + mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 0, null); } else { Log.i(TAG, "There is no active eSIM profiles. Start to switch to removable slot."); - mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId); + mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, null); } } + /** + * Starts switching to the removable slot. + * + * @param physicalSlotId removable physical SIM slot ID. + * @param removedSubInfo if the all of slots have sims, it should remove the one of active sim. + * If the removedSubInfo is null, then use the default value. + * The default value is the removable physical SIM slot and portId 0. + */ + public void run(int physicalSlotId, SubscriptionInfo removedSubInfo) { + mPhysicalSlotId = physicalSlotId; + mRemovedSubInfo = removedSubInfo; + + Log.i(TAG, "Start to switch to removable slot."); + mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo); + } + private void onSwitchToSubscriptionSidecarStateChange() { switch (mSwitchToSubscriptionSidecar.getState()) { case State.SUCCESS: mSwitchToSubscriptionSidecar.reset(); - Log.i( - TAG, + Log.i(TAG, "Successfully disabled eSIM profile. Start to switch to Removable slot."); - mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId); + mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo); break; case State.ERROR: mSwitchToSubscriptionSidecar.reset(); diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java index ccf3f91e868..8938cdbb2e8 100644 --- a/src/com/android/settings/network/UiccSlotUtil.java +++ b/src/com/android/settings/network/UiccSlotUtil.java @@ -19,8 +19,10 @@ package com.android.settings.network; import android.annotation.IntDef; import android.content.Context; import android.provider.Settings; +import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.telephony.UiccSlotInfo; +import android.telephony.UiccSlotMapping; import android.util.Log; import com.android.settingslib.utils.ThreadUtils; @@ -29,17 +31,21 @@ import com.google.common.collect.ImmutableList; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +// ToDo: to do the refactor for renaming public class UiccSlotUtil { private static final String TAG = "UiccSlotUtil"; private static final long DEFAULT_WAIT_AFTER_SWITCH_TIMEOUT_MILLIS = 25 * 1000L; - ; public static final int INVALID_PHYSICAL_SLOT_ID = -1; + public static final int INVALID_PORT_ID = -1; /** * Mode for switching to eSIM slot which decides whether there is cleanup process, e.g. @@ -47,9 +53,9 @@ public class UiccSlotUtil { */ @Retention(RetentionPolicy.SOURCE) @IntDef({ - SwitchingEsimMode.NO_CLEANUP, - SwitchingEsimMode.ASYNC_CLEANUP, - SwitchingEsimMode.SYNC_CLEANUP + SwitchingEsimMode.NO_CLEANUP, + SwitchingEsimMode.ASYNC_CLEANUP, + SwitchingEsimMode.SYNC_CLEANUP }) public @interface SwitchingEsimMode { /** No cleanup process after switching to eSIM slot */ @@ -76,44 +82,108 @@ public class UiccSlotUtil { * 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 slotId the physical removable slot id. * @param context the application context. * @throws UiccSlotsException if there is an error. */ + //ToDo: delete this api and refactor the related code. public static synchronized void switchToRemovableSlot(int slotId, Context context) throws UiccSlotsException { + switchToRemovableSlot(context, slotId, null); + } + + /** + * 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. + * @param removedSubInfo In the DSDS+MEP mode, if the all of slots have sims, it should + * remove the one of active sim. + * If the removedSubInfo is null, then use the default value. + * The default value is the esim slot and portId 0. + * @throws UiccSlotsException if there is an error. + */ + public static synchronized void switchToRemovableSlot(Context context, int slotId, + SubscriptionInfo removedSubInfo) 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].getPorts().stream().findFirst().get().isActive() - && 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].getPorts().stream().findFirst().get().isActive()) { - performSwitchToRemovableSlot(slotId, context); - } - } + int inactiveRemovableSlot = getInactiveRemovableSlot(telMgr.getUiccSlotsInfo(), slotId); + performSwitchToSlot(telMgr, + prepareUiccSlotMappingsForRemovableSlot(telMgr.getSimSlotMapping(), + inactiveRemovableSlot, removedSubInfo, telMgr.isMultiSimEnabled()), + context); } - private static void performSwitchToRemovableSlot(int slotId, Context context) + /** + * Switches to the Euicc slot. It waits for SIM_STATE_LOADED after switch. + * + * @param context the application context. + * @param slotId the Euicc slot id. + * @param port the Euicc slot port id. + * @param removedSubInfo In the DSDS+MEP mode, if the all of slots have sims, it should + * remove the one of active sim. + * If the removedSubInfo is null, then it uses the default value. + * The default value is the esim slot and portId 0. + * @throws UiccSlotsException if there is an error. + */ + public static synchronized void switchToEuiccSlot(Context context, int slotId, int port, + SubscriptionInfo removedSubInfo) throws UiccSlotsException { + if (ThreadUtils.isMainThread()) { + throw new IllegalThreadStateException( + "Do not call switchToRemovableSlot on the main thread."); + } + TelephonyManager telMgr = context.getSystemService(TelephonyManager.class); + Collection uiccSlotMappings = telMgr.getSimSlotMapping(); + Log.i(TAG, "The SimSlotMapping: " + uiccSlotMappings); + + if (isTargetSlotActive(uiccSlotMappings, slotId, port)) { + Log.i(TAG, "The slot is active, then the sim can enable directly."); + return; + } + + Collection newUiccSlotMappings = new ArrayList<>(); + if (!telMgr.isMultiSimEnabled()) { + // In the 'SS mode', the port is 0. + newUiccSlotMappings.add(new UiccSlotMapping(port, slotId, 0)); + } else { + // DSDS+MEP + // The target slot+port is not active, but the all of logical slots are full. It + // needs to replace one of logical slots. + int removedSlot = + (removedSubInfo != null) ? removedSubInfo.getSimSlotIndex() : slotId; + int removedPort = (removedSubInfo != null) ? removedSubInfo.getPortIndex() : 0; + Log.i(TAG, + String.format("Start to set SimSlotMapping from slot%d-port%d to slot%d-port%d", + slotId, port, removedSlot, removedPort)); + newUiccSlotMappings = + uiccSlotMappings.stream().map(uiccSlotMapping -> { + if (uiccSlotMapping.getPhysicalSlotIndex() == removedSlot + && uiccSlotMapping.getPortIndex() == removedPort) { + return new UiccSlotMapping(port, slotId, + uiccSlotMapping.getLogicalSlotIndex()); + } + return uiccSlotMapping; + }).collect(Collectors.toList()); + } + + Log.i(TAG, "The SimSlotMapping: " + newUiccSlotMappings); + performSwitchToSlot(telMgr, newUiccSlotMappings, context); + } + + private static boolean isTargetSlotActive(Collection uiccSlotMappings, + int slotId, int port) { + return uiccSlotMappings.stream() + .anyMatch( + uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex() == slotId + && uiccSlotMapping.getPortIndex() == port); + } + + private static void performSwitchToSlot(TelephonyManager telMgr, + Collection uiccSlotMappings, Context context) throws UiccSlotsException { CarrierConfigChangedReceiver receiver = null; long waitingTimeMillis = @@ -125,7 +195,7 @@ public class UiccSlotUtil { CountDownLatch latch = new CountDownLatch(1); receiver = new CarrierConfigChangedReceiver(latch); receiver.registerOn(context); - switchSlots(context, slotId); + telMgr.setSimSlotMapping(uiccSlotMappings); latch.await(waitingTimeMillis, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -138,22 +208,80 @@ public class UiccSlotUtil { } /** - * 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. + * @param slots The UiccSlotInfo list. + * @param slotId The physical removable slot id. + * @return The inactive physical removable slot id. If the physical removable slot id is + * active, then return -1. + * @throws UiccSlotsException if there is an error. */ - private static void switchSlots(Context context, int... physicalSlots) + private static int getInactiveRemovableSlot(UiccSlotInfo[] slots, int slotId) 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 (slots == null) { + throw new UiccSlotsException("UiccSlotInfo is null"); } - if (!telMgr.switchSlots(physicalSlots)) { - throw new UiccSlotsException("Failed to switch slots"); + if (slotId == INVALID_PHYSICAL_SLOT_ID) { + for (int i = 0; i < slots.length; i++) { + if (slots[i].isRemovable() + && !slots[i].getPorts().stream().findFirst().get().isActive() + && slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_ERROR + && slots[i].getCardStateInfo() != UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) { + return i; + } + } + } else { + if (slotId >= slots.length || !slots[slotId].isRemovable()) { + throw new UiccSlotsException("The given slotId is not a removable slot: " + slotId); + } + if (!slots[slotId].getPorts().stream().findFirst().get().isActive()) { + return slotId; + } } + return INVALID_PHYSICAL_SLOT_ID; + } + + private static Collection prepareUiccSlotMappingsForRemovableSlot( + Collection uiccSlotMappings, int slotId, + SubscriptionInfo removedSubInfo, boolean isMultiSimEnabled) { + if (slotId == INVALID_PHYSICAL_SLOT_ID + || uiccSlotMappings.stream().anyMatch(uiccSlotMapping -> + uiccSlotMapping.getPhysicalSlotIndex() == slotId + && uiccSlotMapping.getPortIndex() == 0)) { + // The slot is invalid slot id, then to skip this. + // The slot is active, then the sim can enable directly. + return uiccSlotMappings; + } + + Collection newUiccSlotMappings = new ArrayList<>(); + if (!isMultiSimEnabled) { + // In the 'SS mode', the port is 0. + newUiccSlotMappings.add(new UiccSlotMapping(0, slotId, 0)); + } else if (removedSubInfo != null) { + // DSDS+MEP + // The target slot+port is not active, but the all of logical slots are full. It + // needs to replace one of logical slots. + Log.i(TAG, + String.format("Start to set SimSlotMapping from slot%d-port%d to slot%d-port%d", + slotId, 0, removedSubInfo.getSimSlotIndex(), + removedSubInfo.getPortIndex())); + newUiccSlotMappings = + uiccSlotMappings.stream().map(uiccSlotMapping -> { + if (uiccSlotMapping.getPhysicalSlotIndex() + == removedSubInfo.getSimSlotIndex() + && uiccSlotMapping.getPortIndex() + == removedSubInfo.getPortIndex()) { + return new UiccSlotMapping(0, slotId, + uiccSlotMapping.getLogicalSlotIndex()); + } + return uiccSlotMapping; + }).collect(Collectors.toList()); + } else { + // DSDS+no MEP + // The removable slot should be in UiccSlotMapping. + newUiccSlotMappings = uiccSlotMappings; + Log.i(TAG, "The removedSubInfo is null"); + } + + Log.i(TAG, "The SimSlotMapping: " + newUiccSlotMappings); + return newUiccSlotMappings; } } diff --git a/src/com/android/settings/network/telephony/ConfirmDialogFragment.java b/src/com/android/settings/network/telephony/ConfirmDialogFragment.java index 1ba99c567bf..04382dad6e6 100644 --- a/src/com/android/settings/network/telephony/ConfirmDialogFragment.java +++ b/src/com/android/settings/network/telephony/ConfirmDialogFragment.java @@ -38,11 +38,15 @@ public class ConfirmDialogFragment extends BaseDialogFragment */ public interface OnConfirmListener { /** - * @param tag The tag in the caller. - * @param confirmed True if the user has clicked the positive button. False if the user has - * clicked the negative button or cancel the dialog. + * @param tag The tag in the caller. + * @param confirmed True if the user has clicked the positive button. False if the + * user has + * clicked the negative button or cancel the dialog. + * @param itemPosition It is the position of item, if user selects one of the list item. + * If the user select "cancel" or the dialog does not have list, then + * the value is -1. */ - void onConfirm(int tag, boolean confirmed); + void onConfirm(int tag, boolean confirmed, int itemPosition); } /** Displays a confirmation dialog which has confirm and cancel buttons. */ @@ -89,19 +93,21 @@ public class ConfirmDialogFragment extends BaseDialogFragment @Override public void onClick(DialogInterface dialog, int which) { - informCaller(which == DialogInterface.BUTTON_POSITIVE); + Log.i(TAG, "dialog onClick =" + which); + + informCaller(which == DialogInterface.BUTTON_POSITIVE, -1); } @Override public void onCancel(DialogInterface dialog) { - informCaller(false); + informCaller(false, -1); } - private void informCaller(boolean confirmed) { + private void informCaller(boolean confirmed, int itemPosition) { OnConfirmListener listener = getListener(OnConfirmListener.class); if (listener == null) { return; } - listener.onConfirm(getTagInCaller(), confirmed); + listener.onConfirm(getTagInCaller(), confirmed, itemPosition); } } diff --git a/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java index f429f8b59c0..8247f63c9f9 100644 --- a/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java +++ b/src/com/android/settings/network/telephony/DeleteEuiccSubscriptionDialogActivity.java @@ -96,7 +96,7 @@ public class DeleteEuiccSubscriptionDialogActivity extends SubscriptionActionDia } @Override - public void onConfirm(int tag, boolean confirmed) { + public void onConfirm(int tag, boolean confirmed, int itemPosition) { if (!confirmed) { finish(); return; diff --git a/src/com/android/settings/network/telephony/EuiccOperationSidecar.java b/src/com/android/settings/network/telephony/EuiccOperationSidecar.java index 05c866e7e9a..d1d362b8e76 100644 --- a/src/com/android/settings/network/telephony/EuiccOperationSidecar.java +++ b/src/com/android/settings/network/telephony/EuiccOperationSidecar.java @@ -24,10 +24,12 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.SystemClock; +import android.telephony.TelephonyManager; import android.telephony.euicc.EuiccManager; import android.util.Log; import com.android.settings.SidecarFragment; +import com.android.settings.network.SwitchSlotSidecar; import java.util.concurrent.atomic.AtomicInteger; @@ -37,7 +39,8 @@ import java.util.concurrent.atomic.AtomicInteger; * should implement its own get() function to return an instance of that class, and implement the * functional class like run() to actually trigger the function in EuiccManager. */ -public abstract class EuiccOperationSidecar extends SidecarFragment { +public abstract class EuiccOperationSidecar extends SidecarFragment + implements SidecarFragment.Listener{ private static final String TAG = "EuiccOperationSidecar"; private static final int REQUEST_CODE = 0; private static final String EXTRA_OP_ID = "op_id"; @@ -45,6 +48,9 @@ public abstract class EuiccOperationSidecar extends SidecarFragment { new AtomicInteger((int) SystemClock.elapsedRealtime()); protected EuiccManager mEuiccManager; + protected TelephonyManager mTelephonyManager; + protected SwitchSlotSidecar mSwitchSlotSidecar; + private int mResultCode; private int mDetailedCode; @@ -107,6 +113,8 @@ public abstract class EuiccOperationSidecar extends SidecarFragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mEuiccManager = getContext().getSystemService(EuiccManager.class); + mTelephonyManager = getContext().getSystemService(TelephonyManager.class); + mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager()); getContext() .getApplicationContext() @@ -118,12 +126,42 @@ public abstract class EuiccOperationSidecar extends SidecarFragment { Context.RECEIVER_EXPORTED); } + @Override + public void onResume() { + super.onResume(); + mSwitchSlotSidecar.addListener(this); + } + + @Override + public void onPause() { + mSwitchSlotSidecar.removeListener(this); + super.onPause(); + } + @Override public void onDestroy() { getContext().getApplicationContext().unregisterReceiver(mReceiver); super.onDestroy(); } + @Override + public void onStateChange(SidecarFragment fragment) { + if (fragment == mSwitchSlotSidecar) { + switch (mSwitchSlotSidecar.getState()) { + case State.SUCCESS: + mSwitchSlotSidecar.reset(); + Log.i(TAG, "mSwitchSlotSidecar SUCCESS"); + break; + case State.ERROR: + mSwitchSlotSidecar.reset(); + Log.i(TAG, "mSwitchSlotSidecar ERROR"); + break; + } + } else { + Log.wtf(TAG, "Received state change from a sidecar not expected."); + } + } + public int getResultCode() { return mResultCode; } diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java index 2f6e8a12805..f8dee8b308a 100644 --- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java +++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java @@ -23,6 +23,7 @@ import android.os.UserManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.UiccCardInfo; import android.telephony.UiccSlotInfo; import android.text.TextUtils; import android.util.Log; @@ -40,7 +41,9 @@ import com.android.settings.sim.SimActivationNotifier; import com.google.common.collect.ImmutableList; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; /** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity @@ -55,6 +58,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc 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; + private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP = 5; + // Number of SIMs for DSDS private static final int NUM_OF_SIMS_FOR_DSDS = 2; // Support RTL mode @@ -85,11 +90,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc private boolean mIsEsimOperation; private TelephonyManager mTelMgr; private boolean isRtlMode; + private List mActiveSubInfos; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Intent intent = getIntent(); int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); mTelMgr = getSystemService(TelephonyManager.class); @@ -107,6 +112,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc return; } + mActiveSubInfos = SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager); mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId); mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded(); mSwitchToEuiccSubscriptionSidecar = @@ -116,6 +122,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc mEnable = intent.getBooleanExtra(ARG_enable, true); isRtlMode = getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + Log.i(TAG, "isMultipleEnabledProfilesSupported():" + isMultipleEnabledProfilesSupported()); if (savedInstanceState == null) { if (mEnable) { @@ -154,7 +161,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc } @Override - public void onConfirm(int tag, boolean confirmed) { + public void onConfirm(int tag, boolean confirmed, int itemPosition) { if (!confirmed && tag != DIALOG_TAG_ENABLE_DSDS_CONFIRMATION && tag != DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION) { @@ -162,14 +169,16 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc return; } + SubscriptionInfo removedSubInfo = null; switch (tag) { case DIALOG_TAG_DISABLE_SIM_CONFIRMATION: if (mIsEsimOperation) { Log.i(TAG, "Disabling the eSIM profile."); showProgressDialog( getString(R.string.privileged_action_disable_sub_dialog_progress)); + int port = mSubInfo != null ? mSubInfo.getPortIndex() : 0; mSwitchToEuiccSubscriptionSidecar.run( - SubscriptionManager.INVALID_SUBSCRIPTION_ID); + SubscriptionManager.INVALID_SUBSCRIPTION_ID, port, null); return; } Log.i(TAG, "Disabling the pSIM profile."); @@ -201,6 +210,11 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc SimActivationNotifier.setShowSimSettingsNotification(this, true); mTelMgr.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS); break; + case DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP: + if (itemPosition != -1) { + removedSubInfo = (mActiveSubInfos != null) ? mActiveSubInfos.get(itemPosition) + : null; + } case DIALOG_TAG_ENABLE_SIM_CONFIRMATION: Log.i(TAG, "User confirmed to enable the subscription."); if (mIsEsimOperation) { @@ -209,12 +223,15 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc R.string.sim_action_switch_sub_dialog_progress, SubscriptionUtil.getUniqueSubscriptionDisplayName( mSubInfo, this))); - mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId()); + mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(), + UiccSlotUtil.INVALID_PORT_ID, + removedSubInfo); return; } showProgressDialog( getString(R.string.sim_action_enabling_sim_without_carrier_name)); - mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID); + mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID, + removedSubInfo); break; default: Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag); @@ -225,8 +242,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc private void handleSwitchToEuiccSubscriptionSidecarStateChange() { switch (mSwitchToEuiccSubscriptionSidecar.getState()) { case SidecarFragment.State.SUCCESS: - Log.i( - TAG, + Log.i(TAG, String.format( "Successfully %s the eSIM profile.", mEnable ? "enable" : "disable")); @@ -235,8 +251,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc finish(); break; case SidecarFragment.State.ERROR: - Log.i( - TAG, + Log.i(TAG, String.format( "Failed to %s the eSIM profile.", mEnable ? "enable" : "disable")); mSwitchToEuiccSubscriptionSidecar.reset(); @@ -290,7 +305,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc 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()); + mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(), + UiccSlotUtil.INVALID_PORT_ID, null); return; } @@ -305,10 +321,8 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable); finish(); } else { - Log.i( - TAG, - "The device does not support toggling pSIM. It is enough to just " - + "enable the removable slot."); + Log.i(TAG, "The device does not support toggling pSIM. It is enough to just " + + "enable the removable slot."); } } @@ -319,7 +333,10 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc showEnableDsdsConfirmDialog(); return; } - if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()) { + if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled() + && isRemovableSimEnabled()) { + // This case is for switching on psim when device is not multiple enable profile + // supported. Log.i(TAG, "Toggle on pSIM, no dialog displayed."); handleTogglePsimAction(); finish(); @@ -372,27 +389,55 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc } private void showEnableSimConfirmDialog() { - List activeSubs = - SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager); - SubscriptionInfo activeSub = activeSubs.isEmpty() ? null : activeSubs.get(0); - if (activeSub == null) { + if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) { Log.i(TAG, "No active subscriptions available."); showNonSwitchSimConfirmDialog(); return; } - Log.i(TAG, "Found active subscription."); - boolean isBetweenEsim = mIsEsimOperation && activeSub.isEmbedded(); - if (mTelMgr.isMultiSimEnabled() && !isBetweenEsim) { + Log.i(TAG, "mActiveSubInfos:" + mActiveSubInfos); + + boolean isSwitchingBetweenEsims = mIsEsimOperation + && mActiveSubInfos.stream().anyMatch(activeSubInfo -> activeSubInfo.isEmbedded()); + boolean isMultiSimEnabled = mTelMgr.isMultiSimEnabled(); + if (isMultiSimEnabled + && !isMultipleEnabledProfilesSupported() + && !isSwitchingBetweenEsims) { + // Showing the "no switch dialog" for below cases. + // DSDS mode + no MEP + + // (there is the active psim -> esim switch on => active (psim + esim)) showNonSwitchSimConfirmDialog(); return; } + if (isMultiSimEnabled && isMultipleEnabledProfilesSupported()) { + if (mActiveSubInfos.size() < NUM_OF_SIMS_FOR_DSDS) { + // The sim can add into device directly, so showing the "no switch dialog". + // DSDS + MEP + (active sim < NUM_OF_SIMS_FOR_DSDS) + showNonSwitchSimConfirmDialog(); + } else { + // The all of slots have sim, it needs to show the "MEP switch dialog". + // DSDS + MEP + two active sims + showMepSwitchSimConfirmDialog(); + } + return; + } + + // Showing the "switch dialog" for below cases. + // case1: SS mode + psim switch on from esim. + // case2: SS mode + esim switch from psim. + // case3: DSDS mode + No MEP + esim switch on from another esim. + SubscriptionInfo activeSub = + (isMultiSimEnabled && isSwitchingBetweenEsims) + ? mActiveSubInfos.stream() + .filter(activeSubInfo -> activeSubInfo.isEmbedded()) + .findFirst().get() + : mActiveSubInfos.get(0); ConfirmDialogFragment.show( this, ConfirmDialogFragment.OnConfirmListener.class, DIALOG_TAG_ENABLE_SIM_CONFIRMATION, getSwitchSubscriptionTitle(), - getSwitchDialogBodyMsg(activeSub, isBetweenEsim), + getSwitchDialogBodyMsg(activeSub, isSwitchingBetweenEsims), getSwitchDialogPosBtnText(), getString(R.string.sim_action_cancel)); } @@ -408,6 +453,10 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc getString(R.string.sim_action_cancel)); } + private void showMepSwitchSimConfirmDialog() { + Log.i(TAG, "showMepSwitchSimConfirmDialog"); + } + private String getSwitchDialogPosBtnText() { return mIsEsimOperation ? getString( @@ -468,6 +517,20 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc return switchDialogMsg.toString(); } + private ArrayList getSwitchDialogBodyList() { + ArrayList list = new ArrayList(mActiveSubInfos.stream() + .map(subInfo -> { + CharSequence subInfoName = SubscriptionUtil.getUniqueSubscriptionDisplayName( + subInfo, this); + return getString( + R.string.sim_action_switch_sub_dialog_carrier_list_item_for_turning_off, + subInfoName); + }) + .collect(Collectors.toList())); + list.add(getString(R.string.sim_action_cancel)); + return list; + } + private boolean isDsdsConditionSatisfied() { if (mTelMgr.isMultiSimEnabled()) { Log.i(TAG, "DSDS is already enabled. Condition not satisfied."); @@ -477,17 +540,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc Log.i(TAG, "Hardware does not support DSDS."); return false; } - ImmutableList slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr); - boolean isRemovableSimEnabled = - slotInfos.stream() - .anyMatch( - slot -> - slot != null - && slot.isRemovable() - && slot.getPorts().stream().anyMatch( - port -> port.isActive()) - && slot.getCardStateInfo() - == UiccSlotInfo.CARD_STATE_INFO_PRESENT); + boolean isRemovableSimEnabled = isRemovableSimEnabled(); if (mIsEsimOperation && isRemovableSimEnabled) { Log.i(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied."); return true; @@ -496,13 +549,36 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream() .anyMatch(SubscriptionInfo::isEmbedded); if (!mIsEsimOperation && isEsimProfileEnabled) { - Log.i( - TAG, - "Removable SIM operation and eSIM profile is enabled. DSDS condition" - + " satisfied."); + 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; } + + private boolean isRemovableSimEnabled() { + ImmutableList slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr); + boolean isRemovableSimEnabled = + slotInfos.stream() + .anyMatch( + slot -> slot != null + && slot.isRemovable() + && slot.getPorts().stream().anyMatch( + port -> port.isActive()) + && slot.getCardStateInfo() + == UiccSlotInfo.CARD_STATE_INFO_PRESENT); + Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled); + return isRemovableSimEnabled; + } + + private boolean isMultipleEnabledProfilesSupported() { + List cardInfos = mTelMgr.getUiccCardsInfo(); + if (cardInfos == null) { + Log.w(TAG, "UICC cards info list is empty."); + return false; + } + return cardInfos.stream().anyMatch( + cardInfo -> cardInfo.isMultipleEnabledProfilesSupported()); + } } diff --git a/src/com/android/settings/sim/DsdsDialogActivity.java b/src/com/android/settings/sim/DsdsDialogActivity.java index 62a6995a54c..7d9a43bc86d 100644 --- a/src/com/android/settings/sim/DsdsDialogActivity.java +++ b/src/com/android/settings/sim/DsdsDialogActivity.java @@ -85,7 +85,7 @@ public class DsdsDialogActivity extends SubscriptionActionDialogActivity } @Override - public void onConfirm(int tag, boolean confirmed) { + public void onConfirm(int tag, boolean confirmed, int itemPosition) { if (!confirmed) { Log.i(TAG, "User cancel the dialog to enable DSDS."); startChooseSimActivity(); diff --git a/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java b/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java index 385deffd2ba..be2fa2d1c6d 100644 --- a/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java +++ b/src/com/android/settings/sim/SwitchToEsimConfirmDialogActivity.java @@ -101,7 +101,7 @@ public class SwitchToEsimConfirmDialogActivity extends SubscriptionActionDialogA } @Override - public void onConfirm(int tag, boolean confirmed) { + public void onConfirm(int tag, boolean confirmed, int itemPosition) { if (!confirmed) { AlertDialogFragment.show( this, From 9ef7f72349eb684c4e9a47f7aa8bd437d6ac5438 Mon Sep 17 00:00:00 2001 From: Jan Tomljanovic Date: Wed, 8 Dec 2021 18:05:31 +0000 Subject: [PATCH 03/12] Toggle Security and Privacy entries depending on SafetyCenter status. Test: atest SettingsUnitTests Bug: 206798563 Change-Id: I4c813a35754fa7ed5db630fa4c41ef14b469878c --- res/xml/top_level_settings.xml | 3 +- ...LevelPrivacyEntryPreferenceController.java | 39 +++++++++ .../safetycenter/SafetyCenterStatus.java | 2 +- ...evelSecurityEntryPreferenceController.java | 6 +- ...lPrivacyEntryPreferenceControllerTest.java | 84 +++++++++++++++++++ ...SecurityEntryPreferenceControllerTest.java | 37 ++++++++ 6 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceController.java create mode 100644 tests/unit/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceControllerTest.java diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml index 6005581d4d9..280c3f31bc3 100644 --- a/res/xml/top_level_settings.xml +++ b/res/xml/top_level_settings.xml @@ -142,7 +142,8 @@ android:order="-40" android:title="@string/privacy_dashboard_title" android:summary="@string/privacy_dashboard_summary" - settings:highlightableMenuKey="@string/menu_key_privacy"/> + settings:highlightableMenuKey="@string/menu_key_privacy" + settings:controller="com.android.settings.privacy.TopLevelPrivacyEntryPreferenceController"/> Date: Fri, 3 Dec 2021 16:35:02 -0500 Subject: [PATCH 04/12] add post_notification permission to manifest so the package can send notifications on T+ Bug: 194833441 Change-Id: I60cfd8e926cf5cf0a113b00437dbd342e7ef95df --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b79b2d82556..b1f05488945 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -110,6 +110,7 @@ + Date: Wed, 8 Dec 2021 20:23:13 +0800 Subject: [PATCH 05/12] Turn off deep link components by default We should only enable necessary components when device supports the split activity feature. Test: Enable/Disable components and make sure things work well. Bug: 204405245 Change-Id: Icb03e121eed0e584bebe1c9c838d0f4f44015abf --- AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 22afa53218b..24c04d36a2b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -151,6 +151,7 @@ android:taskAffinity="" android:launchMode="singleTask" android:exported="true" + android:enabled="false" android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout" android:permission="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK"> @@ -308,6 +309,7 @@ From b5bb0e85a1cf3101d08dc3f30d8e889bb36ed03e Mon Sep 17 00:00:00 2001 From: Sunny Shao Date: Sat, 20 Nov 2021 21:26:42 +0800 Subject: [PATCH 06/12] Move the text under slider bar to the description under title - Make this change after discussed with UX team. Fixes: 206737771 Test: manual test Change-Id: I9517933312d8db400f6d40b4124815ad275014ec --- res/layout/font_size_preview.xml | 1 + res/layout/suw_font_size_fragment.xml | 6 ----- res/layout/suw_screen_zoom_fragment.xml | 6 ----- res/values-sw600dp/config.xml | 2 ++ res/values-sw600dp/dimens.xml | 3 +++ res/values/config.xml | 3 +++ ...ilityScreenSizeForSetupWizardActivity.java | 4 ++-- ...tSizePreferenceFragmentForSetupWizard.java | 24 +++++++++++++++++++ 8 files changed, 35 insertions(+), 14 deletions(-) diff --git a/res/layout/font_size_preview.xml b/res/layout/font_size_preview.xml index 2b1773bcee3..f916ac41014 100644 --- a/res/layout/font_size_preview.xml +++ b/res/layout/font_size_preview.xml @@ -26,6 +26,7 @@ android:layout_height="wrap_content"> - - diff --git a/res/layout/suw_screen_zoom_fragment.xml b/res/layout/suw_screen_zoom_fragment.xml index 0747381085f..369ff145405 100644 --- a/res/layout/suw_screen_zoom_fragment.xml +++ b/res/layout/suw_screen_zoom_fragment.xml @@ -74,12 +74,6 @@ android:contentDescription="@string/screen_zoom_make_larger_desc" style="@style/screen_size_imageview_style"/> - - diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml index 543dfeeabd3..f22d7bafc85 100644 --- a/res/values-sw600dp/config.xml +++ b/res/values-sw600dp/config.xml @@ -19,4 +19,6 @@ 2 + + true diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 45c28dda461..b3577581884 100755 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -63,6 +63,9 @@ 0dp 64dp + + 32dp + 40dp 40dp diff --git a/res/values/config.xml b/res/values/config.xml index 35e2ce33341..987eaf97254 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -532,6 +532,9 @@ false + + false + diff --git a/src/com/android/settings/accessibility/AccessibilityScreenSizeForSetupWizardActivity.java b/src/com/android/settings/accessibility/AccessibilityScreenSizeForSetupWizardActivity.java index 10a0bce5c5a..7c8076f3bd9 100644 --- a/src/com/android/settings/accessibility/AccessibilityScreenSizeForSetupWizardActivity.java +++ b/src/com/android/settings/accessibility/AccessibilityScreenSizeForSetupWizardActivity.java @@ -120,8 +120,8 @@ public class AccessibilityScreenSizeForSetupWizardActivity extends InstrumentedA : R.string.screen_zoom_title); ((TextView) findViewById(R.id.sud_layout_subtitle)).setText( getFragmentType(getIntent()) == FragmentType.FONT_SIZE - ? R.string.short_summary_font_size - : R.string.screen_zoom_short_summary); + ? R.string.font_size_summary + : R.string.screen_zoom_summary); } private boolean isSuwSupportedTwoPanes() { diff --git a/src/com/android/settings/display/FontSizePreferenceFragmentForSetupWizard.java b/src/com/android/settings/display/FontSizePreferenceFragmentForSetupWizard.java index 627f107a69f..5bfce1875f4 100644 --- a/src/com/android/settings/display/FontSizePreferenceFragmentForSetupWizard.java +++ b/src/com/android/settings/display/FontSizePreferenceFragmentForSetupWizard.java @@ -17,6 +17,13 @@ package com.android.settings.display; import android.app.settings.SettingsEnums; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import androidx.viewpager.widget.ViewPager; import com.android.settings.R; @@ -33,6 +40,23 @@ public class FontSizePreferenceFragmentForSetupWizard return SettingsEnums.SUW_ACCESSIBILITY_FONT_SIZE; } + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View root = super.onCreateView(inflater, container, savedInstanceState); + if (getResources().getBoolean(R.bool.config_supported_large_screen)) { + final ViewPager viewPager = root.findViewById(R.id.preview_pager); + final View view = (View) viewPager.getAdapter().instantiateItem(viewPager, + viewPager.getCurrentItem()); + final LinearLayout layout = view.findViewById(R.id.font_size_preview_text_group); + final int paddingStart = getResources().getDimensionPixelSize( + R.dimen.font_size_preview_padding_start); + layout.setPaddingRelative(paddingStart, layout.getPaddingTop(), + layout.getPaddingEnd(), layout.getPaddingBottom()); + } + return root; + } + @Override public void onStop() { // Log the final choice in value if it's different from the previous value. From 70172863a7c80008853cf059ce3f01e4b495b1f2 Mon Sep 17 00:00:00 2001 From: Rubin Xu Date: Thu, 9 Dec 2021 13:27:03 +0000 Subject: [PATCH 07/12] Remove subtext from work challenge screens Bug: 201045654 Test: manual Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.password Change-Id: Ibd65e27d5ed2762d04aa7750908506d0353a0727 --- res/values/strings.xml | 19 ------------ .../settings/password/ChooseLockPassword.java | 30 +++++++------------ .../settings/password/ChooseLockPattern.java | 27 ++++++++--------- .../password/SetupChooseLockPattern.java | 8 +---- 4 files changed, 24 insertions(+), 60 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 17a1ece85ba..c382bee4b59 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4386,25 +4386,6 @@ Set a work pattern - - For added security, set a password to unlock the phone - - For added security, set a PIN to unlock the phone - - For added security, set a pattern to unlock the phone - - For added security, set a password to unlock the tablet - - For added security, set a PIN to unlock the tablet - - For added security, set a pattern to unlock the tablet - - For added security, set a password to unlock the device - - For added security, set a PIN to unlock the device - - For added security, set a pattern to unlock the device - To use fingerprint, set password diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index c03362510d0..a2f9922531b 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -268,9 +268,7 @@ public class ChooseLockPassword extends SettingsActivity { R.string.lockpassword_choose_your_pin_header_for_fingerprint, R.string.lockpassword_choose_your_pin_header_for_face, R.string.lockpassword_choose_your_pin_header_for_biometrics, - R.string.lockpassword_choose_password_description, R.string.lock_settings_picker_biometrics_added_security_message, - R.string.lockpassword_choose_pin_description, R.string.lock_settings_picker_biometrics_added_security_message, R.string.next_label), @@ -287,8 +285,6 @@ public class ChooseLockPassword extends SettingsActivity { R.string.lockpassword_confirm_your_pin_header, 0, 0, - 0, - 0, R.string.lockpassword_confirm_label), ConfirmWrong( @@ -304,8 +300,6 @@ public class ChooseLockPassword extends SettingsActivity { R.string.lockpassword_confirm_pins_dont_match, 0, 0, - 0, - 0, R.string.lockpassword_confirm_label); Stage(int hintInAlpha, @@ -318,9 +312,7 @@ public class ChooseLockPassword extends SettingsActivity { int hintInNumericForFingerprint, int hintInNumericForFace, int hintInNumericForBiometrics, - int messageInAlpha, int messageInAlphaForBiometrics, - int messageInNumeric, int messageInNumericForBiometrics, int nextButtonText) { @@ -336,10 +328,7 @@ public class ChooseLockPassword extends SettingsActivity { this.numericHintForFace = hintInNumericForFace; this.numericHintForBiometrics = hintInNumericForBiometrics; - this.alphaMessage = messageInAlpha; this.alphaMessageForBiometrics = messageInAlphaForBiometrics; - - this.numericMessage = messageInNumeric; this.numericMessageForBiometrics = messageInNumericForBiometrics; this.buttonText = nextButtonText; @@ -365,11 +354,9 @@ public class ChooseLockPassword extends SettingsActivity { public final int numericHintForBiometrics; // Password description - public final int alphaMessage; public final int alphaMessageForBiometrics; // PIN description - public final int numericMessage; public final int numericMessageForBiometrics; public final int buttonText; @@ -407,7 +394,7 @@ public class ChooseLockPassword extends SettingsActivity { case TYPE_NONE: default: - return isAlpha ? alphaMessage : numericMessage; + return 0; } } } @@ -869,12 +856,17 @@ public class ChooseLockPassword extends SettingsActivity { setNextEnabled(canInput && length >= LockPatternUtils.MIN_LOCK_PASSWORD_SIZE); mSkipOrClearButton.setVisibility(toVisibility(canInput && length > 0)); } - int message = mUiStage.getMessage(mIsAlphaMode, getStageType()); - if (message != 0) { - mMessage.setVisibility(View.VISIBLE); - mMessage.setText(message); + final int stage = getStageType(); + if (getStageType() != Stage.TYPE_NONE) { + int message = mUiStage.getMessage(mIsAlphaMode, stage); + if (message != 0) { + mMessage.setVisibility(View.VISIBLE); + mMessage.setText(message); + } else { + mMessage.setVisibility(View.INVISIBLE); + } } else { - mMessage.setVisibility(View.INVISIBLE); + mMessage.setVisibility(View.GONE); } setNextText(mUiStage.buttonText); diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java index 016906aea10..3e7622c6b66 100644 --- a/src/com/android/settings/password/ChooseLockPattern.java +++ b/src/com/android/settings/password/ChooseLockPattern.java @@ -363,54 +363,49 @@ public class ChooseLockPattern extends SettingsActivity { Introduction( R.string.lock_settings_picker_biometrics_added_security_message, - R.string.lockpattern_choose_pattern_description, R.string.lockpattern_recording_intro_header, LeftButtonMode.Gone, RightButtonMode.ContinueDisabled, ID_EMPTY_MESSAGE, true), HelpScreen( - ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_settings_help_how_to_record, + ID_EMPTY_MESSAGE, R.string.lockpattern_settings_help_how_to_record, LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false), ChoiceTooShort( R.string.lock_settings_picker_biometrics_added_security_message, - R.string.lockpattern_choose_pattern_description, R.string.lockpattern_recording_incorrect_too_short, LeftButtonMode.Retry, RightButtonMode.ContinueDisabled, ID_EMPTY_MESSAGE, true), FirstChoiceValid( R.string.lock_settings_picker_biometrics_added_security_message, - R.string.lockpattern_choose_pattern_description, R.string.lockpattern_pattern_entered_header, LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false), NeedToConfirm( - ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_confirm, + ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_confirm, LeftButtonMode.Gone, RightButtonMode.ConfirmDisabled, ID_EMPTY_MESSAGE, true), ConfirmWrong( - ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_unlock_wrong, + ID_EMPTY_MESSAGE, R.string.lockpattern_need_to_unlock_wrong, LeftButtonMode.Gone, RightButtonMode.ConfirmDisabled, ID_EMPTY_MESSAGE, true), ChoiceConfirmed( - ID_EMPTY_MESSAGE, ID_EMPTY_MESSAGE, R.string.lockpattern_pattern_confirmed_header, + ID_EMPTY_MESSAGE, R.string.lockpattern_pattern_confirmed_header, LeftButtonMode.Gone, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false); /** * @param messageForBiometrics The message displayed at the top, above header for * fingerprint flow. - * @param message The message displayed at the top. * @param headerMessage The message displayed at the top. * @param leftMode The mode of the left button. * @param rightMode The mode of the right button. * @param footerMessage The footer message. * @param patternEnabled Whether the pattern widget is enabled. */ - Stage(int messageForBiometrics, int message, int headerMessage, + Stage(int messageForBiometrics, int headerMessage, LeftButtonMode leftMode, RightButtonMode rightMode, int footerMessage, boolean patternEnabled) { this.headerMessage = headerMessage; this.messageForBiometrics = messageForBiometrics; - this.message = message; this.leftMode = leftMode; this.rightMode = rightMode; this.footerMessage = footerMessage; @@ -419,7 +414,6 @@ public class ChooseLockPattern extends SettingsActivity { final int headerMessage; final int messageForBiometrics; - final int message; final LeftButtonMode leftMode; final RightButtonMode rightMode; final int footerMessage; @@ -735,11 +729,14 @@ public class ChooseLockPattern extends SettingsActivity { } final GlifLayout layout = getActivity().findViewById(R.id.setup_wizard_layout); final boolean forAnyBiometric = mForFingerprint || mForFace || mForBiometrics; - int message = forAnyBiometric ? stage.messageForBiometrics : stage.message; - if (message == ID_EMPTY_MESSAGE) { - layout.setDescriptionText(""); + if (forAnyBiometric) { + if (stage.messageForBiometrics == ID_EMPTY_MESSAGE) { + layout.setDescriptionText(""); + } else { + layout.setDescriptionText(stage.messageForBiometrics); + } } else { - layout.setDescriptionText(message); + layout.getDescriptionTextView().setVisibility(View.GONE); } if (stage.footerMessage == ID_EMPTY_MESSAGE) { mFooterText.setText(""); diff --git a/src/com/android/settings/password/SetupChooseLockPattern.java b/src/com/android/settings/password/SetupChooseLockPattern.java index 70cd6f2aebb..7151c6d89cc 100644 --- a/src/com/android/settings/password/SetupChooseLockPattern.java +++ b/src/com/android/settings/password/SetupChooseLockPattern.java @@ -142,14 +142,8 @@ public class SetupChooseLockPattern extends ChooseLockPattern { mLeftButtonIsSkip = false; } - // Show generic pattern message when pattern lock screen launch in Setup wizard flow - // before fingerprint and face setup. final GlifLayout layout = getActivity().findViewById(R.id.setup_wizard_layout); - if (stage.message == ID_EMPTY_MESSAGE) { - layout.setDescriptionText(""); - } else { - layout.setDescriptionText(stage.message); - } + layout.setDescriptionText(""); } @Override From 493e889dd67b684a25671e76a336c2d39d4ae7aa Mon Sep 17 00:00:00 2001 From: Joe Bolinger Date: Thu, 9 Dec 2021 19:21:44 +0000 Subject: [PATCH 08/12] Update remove face enrollment strings for convenience. Fix: 209877102 Test: manual (enroll & delete) Change-Id: Idb3a8d3622574edc47673e8fe6a72a5b9d449c7b --- res/values/strings.xml | 8 +++----- .../settings/biometrics/BiometricUtils.java | 16 ++++++++++++++++ ...SettingsRemoveButtonPreferenceController.java | 11 ++++++++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index b566d982523..da5926a2a84 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -871,11 +871,9 @@ Delete face model? - Your face model will be permanently and securely deleted. After deletion, you will need your PIN, pattern, or password to unlock your phone or for authentication in apps. - - Delete face model? - - Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your fingerprint, PIN, pattern, or password to unlock your phone or for authentication in apps. + Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone or for authentication in apps. + + Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone. Use Face Unlock to unlock your phone diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java index 7dd63850dee..5ee788095b4 100644 --- a/src/com/android/settings/biometrics/BiometricUtils.java +++ b/src/com/android/settings/biometrics/BiometricUtils.java @@ -22,6 +22,9 @@ import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.hardware.biometrics.SensorProperties; +import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorPropertiesInternal; import android.os.storage.StorageManager; import android.util.Log; import android.view.Surface; @@ -273,4 +276,17 @@ public class BiometricUtils { public static boolean isReverseLandscape(@NonNull Context context) { return context.getDisplay().getRotation() == Surface.ROTATION_270; } + + /** + * @param faceManager + * @return True if at least one sensor is set as a convenience. + */ + public static boolean isConvenience(@NonNull FaceManager faceManager) { + for (FaceSensorPropertiesInternal props : faceManager.getSensorPropertiesInternal()) { + if (props.sensorStrength == SensorProperties.STRENGTH_CONVENIENCE) { + return true; + } + } + return false; + } } diff --git a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java index d8ff4822a4e..616b736b8d6 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java @@ -33,6 +33,7 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.biometrics.BiometricUtils; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.overlay.FeatureFactory; @@ -56,6 +57,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference public static class ConfirmRemoveDialog extends InstrumentedDialogFragment { + private boolean mIsConvenience; private DialogInterface.OnClickListener mOnClickListener; @Override @@ -68,7 +70,9 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.security_settings_face_settings_remove_dialog_title) - .setMessage(R.string.security_settings_face_settings_remove_dialog_details) + .setMessage(mIsConvenience + ? R.string.security_settings_face_settings_remove_dialog_details_convenience + : R.string.security_settings_face_settings_remove_dialog_details) .setPositiveButton(R.string.delete, mOnClickListener) .setNegativeButton(R.string.cancel, mOnClickListener); AlertDialog dialog = builder.create(); @@ -76,6 +80,10 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference return dialog; } + public void setIsConvenience(boolean isConvenience) { + mIsConvenience = isConvenience; + } + public void setOnClickListener(DialogInterface.OnClickListener listener) { mOnClickListener = listener; } @@ -197,6 +205,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference mRemoving = true; ConfirmRemoveDialog dialog = new ConfirmRemoveDialog(); dialog.setOnClickListener(mOnClickListener); + dialog.setIsConvenience(BiometricUtils.isConvenience(mFaceManager)); dialog.show(mActivity.getSupportFragmentManager(), ConfirmRemoveDialog.class.getName()); } } From 22ab05b42088626a201b01f989f09df1e29f62fb Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Wed, 8 Dec 2021 22:51:43 +0800 Subject: [PATCH 09/12] switch SIM refactor to support MEP To create new dialog for MEP. It is a simple UI for testing, not the final version. Bug: 199902896 Test: local build pass. Change-Id: Ief4299e775c0758e4b886d5eff13bd482f8c8ab3 --- AndroidManifest.xml | 8 +- ...og_multiple_enabled_profiles_supported.xml | 57 ++++++++++++ .../telephony/AlertDialogFragment.java | 11 +-- .../network/telephony/BaseDialogFragment.java | 4 +- .../telephony/ConfirmDialogFragment.java | 88 +++++++++++++++++-- .../SubscriptionActionDialogActivity.java | 5 +- .../ToggleSubscriptionDialogActivity.java | 25 ++++++ .../settings/sim/SimDialogActivity.java | 6 +- 8 files changed, 181 insertions(+), 23 deletions(-) create mode 100644 res/layout/sim_confirm_dialog_multiple_enabled_profiles_supported.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 083d8b9fb7c..99b62a3cd34 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -659,12 +659,12 @@ + android:theme="@style/Theme.AlertDialog"/> + android:theme="@style/Theme.AlertDialog"/> + android:theme="@style/Theme.AlertDialog"/> + android:theme="@style/Theme.AlertDialog"/> diff --git a/res/layout/sim_confirm_dialog_multiple_enabled_profiles_supported.xml b/res/layout/sim_confirm_dialog_multiple_enabled_profiles_supported.xml new file mode 100644 index 00000000000..44044820653 --- /dev/null +++ b/res/layout/sim_confirm_dialog_multiple_enabled_profiles_supported.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/com/android/settings/network/telephony/AlertDialogFragment.java b/src/com/android/settings/network/telephony/AlertDialogFragment.java index 5940789673e..aaccc2db837 100644 --- a/src/com/android/settings/network/telephony/AlertDialogFragment.java +++ b/src/com/android/settings/network/telephony/AlertDialogFragment.java @@ -16,13 +16,14 @@ package com.android.settings.network.telephony; -import android.app.Activity; -import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; import android.text.TextUtils; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; + /** Fragment to show an alert dialog which only has the positive button. */ public class AlertDialogFragment extends BaseDialogFragment implements DialogInterface.OnClickListener { @@ -37,13 +38,13 @@ public class AlertDialogFragment extends BaseDialogFragment * @param title * @param msg */ - public static void show(Activity activity, String title, String msg) { + public static void show(FragmentActivity activity, String title, String msg) { AlertDialogFragment fragment = new AlertDialogFragment(); Bundle arguments = new Bundle(); arguments.putString(ARG_TITLE, title); arguments.putString(ARG_MSG, msg); fragment.setArguments(arguments); - fragment.show(activity.getFragmentManager(), TAG); + fragment.show(activity.getSupportFragmentManager(), TAG); } @Override @@ -55,7 +56,7 @@ public class AlertDialogFragment extends BaseDialogFragment if (!TextUtils.isEmpty(getArguments().getString(ARG_MSG))) { builder.setMessage(getArguments().getString(ARG_MSG)); } - return builder.show(); + return builder.create(); } @Override diff --git a/src/com/android/settings/network/telephony/BaseDialogFragment.java b/src/com/android/settings/network/telephony/BaseDialogFragment.java index 7da325910e9..0465cef152b 100644 --- a/src/com/android/settings/network/telephony/BaseDialogFragment.java +++ b/src/com/android/settings/network/telephony/BaseDialogFragment.java @@ -17,11 +17,11 @@ package com.android.settings.network.telephony; import android.app.Activity; -import android.app.DialogFragment; -import android.app.Fragment; import android.os.Bundle; import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; /** * Base dialog fragment class with the functionality to make a fragment or an activity as a listener diff --git a/src/com/android/settings/network/telephony/ConfirmDialogFragment.java b/src/com/android/settings/network/telephony/ConfirmDialogFragment.java index 04382dad6e6..bad981a5c4c 100644 --- a/src/com/android/settings/network/telephony/ConfirmDialogFragment.java +++ b/src/com/android/settings/network/telephony/ConfirmDialogFragment.java @@ -16,13 +16,24 @@ package com.android.settings.network.telephony; -import android.app.Activity; -import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; + +import com.android.settings.R; + +import java.util.ArrayList; /** Fragment to show a confirm dialog. The caller should implement onConfirmListener. */ public class ConfirmDialogFragment extends BaseDialogFragment @@ -32,6 +43,7 @@ public class ConfirmDialogFragment extends BaseDialogFragment private static final String ARG_MSG = "msg"; private static final String ARG_POS_BUTTON_STRING = "pos_button_string"; private static final String ARG_NEG_BUTTON_STRING = "neg_button_string"; + private static final String ARG_LIST = "list"; /** * Interface defining the method that will be invoked when the user has done with the dialog. @@ -51,7 +63,7 @@ public class ConfirmDialogFragment extends BaseDialogFragment /** Displays a confirmation dialog which has confirm and cancel buttons. */ public static void show( - Activity activity, + FragmentActivity activity, Class callbackInterfaceClass, int tagInCaller, String title, @@ -66,7 +78,29 @@ public class ConfirmDialogFragment extends BaseDialogFragment arguments.putString(ARG_NEG_BUTTON_STRING, negButtonString); setListener(activity, null, callbackInterfaceClass, tagInCaller, arguments); fragment.setArguments(arguments); - fragment.show(activity.getFragmentManager(), TAG); + fragment.show(activity.getSupportFragmentManager(), TAG); + } + + /** Displays a confirmation dialog which has confirm and cancel buttons and carrier list.*/ + public static void show( + FragmentActivity activity, + Class callbackInterfaceClass, + int tagInCaller, + String title, + String msg, + String posButtonString, + String negButtonString, + ArrayList list) { + ConfirmDialogFragment fragment = new ConfirmDialogFragment(); + Bundle arguments = new Bundle(); + arguments.putString(ARG_TITLE, title); + arguments.putCharSequence(ARG_MSG, msg); + arguments.putString(ARG_POS_BUTTON_STRING, posButtonString); + arguments.putString(ARG_NEG_BUTTON_STRING, negButtonString); + arguments.putStringArrayList(ARG_LIST, list); + setListener(activity, null, callbackInterfaceClass, tagInCaller, arguments); + fragment.setArguments(arguments); + fragment.show(activity.getSupportFragmentManager(), TAG); } @Override @@ -75,18 +109,56 @@ public class ConfirmDialogFragment extends BaseDialogFragment String message = getArguments().getString(ARG_MSG); String posBtnString = getArguments().getString(ARG_POS_BUTTON_STRING); String negBtnString = getArguments().getString(ARG_NEG_BUTTON_STRING); + ArrayList list = getArguments().getStringArrayList(ARG_LIST); - Log.i("Showing dialog with title = %s", title); + Log.i(TAG, "Showing dialog with title =" + title); AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) .setTitle(title) .setPositiveButton(posBtnString, this) .setNegativeButton(negBtnString, this); - if (!TextUtils.isEmpty(message)) { - builder.setMessage(message); + if (list != null && !list.isEmpty()) { + Log.i(TAG, "list =" + list.toString()); + + View content = LayoutInflater.from(getContext()).inflate( + R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null); + + TextView dialogMessage = content.findViewById(R.id.msg); + if (!TextUtils.isEmpty(message) && dialogMessage != null) { + dialogMessage.setText(message); + } + + final ArrayAdapter arrayAdapterItems = new ArrayAdapter( + getContext(), android.R.layout.select_dialog_item, list); + final ListView lvItems = content.findViewById(R.id.carrier_list); + if (lvItems != null) { + lvItems.setAdapter(arrayAdapterItems); + lvItems.setChoiceMode(ListView.CHOICE_MODE_NONE); + lvItems.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, + long id) { + Log.i(TAG, "list onClick =" + position); + Log.i(TAG, "list item =" + list.get(position)); + + if (position == list.size() - 1) { + // user select the "cancel" item; + informCaller(false, -1); + } else { + informCaller(true, position); + } + } + }); + } + builder.setView(content); + } else { + if (!TextUtils.isEmpty(message)) { + builder.setMessage(message); + } } - AlertDialog dialog = builder.show(); + + AlertDialog dialog = builder.create(); dialog.setCanceledOnTouchOutside(false); return dialog; } diff --git a/src/com/android/settings/network/telephony/SubscriptionActionDialogActivity.java b/src/com/android/settings/network/telephony/SubscriptionActionDialogActivity.java index 7ff0d9a874c..288f1ac1e53 100644 --- a/src/com/android/settings/network/telephony/SubscriptionActionDialogActivity.java +++ b/src/com/android/settings/network/telephony/SubscriptionActionDialogActivity.java @@ -16,12 +16,13 @@ package com.android.settings.network.telephony; -import android.app.Activity; import android.os.Bundle; import android.telephony.SubscriptionManager; +import androidx.fragment.app.FragmentActivity; + /** The base class for subscription action dialogs */ -public class SubscriptionActionDialogActivity extends Activity { +public class SubscriptionActionDialogActivity extends FragmentActivity { private static final String TAG = "SubscriptionActionDialogActivity"; // Arguments diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java index f8dee8b308a..cc2986d5344 100644 --- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java +++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java @@ -455,6 +455,31 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc private void showMepSwitchSimConfirmDialog() { Log.i(TAG, "showMepSwitchSimConfirmDialog"); + final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName( + mSubInfo, this); + String title = getString(R.string.sim_action_switch_sub_dialog_mep_title, displayName); + final StringBuilder switchDialogMsg = new StringBuilder(); + switchDialogMsg.append( + getString(R.string.sim_action_switch_sub_dialog_mep_text, displayName)); + if (isRtlMode) { + /* There are two lines of message in the dialog, and the RTL symbols must be added + * before and after each sentence, so use the line break symbol to find the position. + * (Each message are all with two line break symbols) + */ + switchDialogMsg.insert(0, RTL_MARK) + .insert(switchDialogMsg.indexOf(LINE_BREAK) - LINE_BREAK_OFFSET_ONE, RTL_MARK) + .insert(switchDialogMsg.indexOf(LINE_BREAK) + LINE_BREAK_OFFSET_TWO, RTL_MARK) + .insert(switchDialogMsg.length(), RTL_MARK); + } + ConfirmDialogFragment.show( + this, + ConfirmDialogFragment.OnConfirmListener.class, + DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP, + title, + switchDialogMsg.toString(), + null, + null, + getSwitchDialogBodyList()); } private String getSwitchDialogPosBtnText() { diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java index e5457ae3459..f9aca77d20b 100644 --- a/src/com/android/settings/sim/SimDialogActivity.java +++ b/src/com/android/settings/sim/SimDialogActivity.java @@ -160,8 +160,10 @@ public class SimDialogActivity extends FragmentActivity { final TelephonyManager telephonyManager = getSystemService( TelephonyManager.class).createForSubscriptionId(subId); subscriptionManager.setDefaultDataSubId(subId); - telephonyManager.setDataEnabled(true); - Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show(); + if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + telephonyManager.setDataEnabled(true); + Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show(); + } } private void setDefaultCallsSubId(final int subId) { From bd8f45b9d42b8235fcd896310c57910770b957a3 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Fri, 10 Dec 2021 11:36:55 +0800 Subject: [PATCH 10/12] Fix the onCreate crash happened among zen mode rule pages Fix: 199229588 Test: manual Change-Id: I7b0116de8d9760f420c026a8d65b019e6d2d00df --- .../settings/notification/zen/ZenModeRuleSettingsBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java index 170c699fa8c..5ce8b48b27c 100644 --- a/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java +++ b/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java @@ -59,6 +59,7 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase { @Override public void onCreate(Bundle icicle) { + super.onCreate(icicle); mContext = getActivity(); final Intent intent = getActivity().getIntent(); @@ -81,7 +82,6 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase { return; } - super.onCreate(icicle); mCustomBehaviorPreference = getPreferenceScreen().findPreference(CUSTOM_BEHAVIOR_KEY); mCustomBehaviorPreference.setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { From 4a7f809e41060ca602777be220f47c0bc97de61e Mon Sep 17 00:00:00 2001 From: tom hsu Date: Tue, 2 Nov 2021 20:19:23 +0800 Subject: [PATCH 11/12] [Settings] Avoid crash from rotation screen. Bug: b/200822579 Test: Local test Change-Id: Ie25b2ab4284d47abdd5db23676d05b6d547b2a73 Merged-In: Ie25b2ab4284d47abdd5db23676d05b6d547b2a73 --- AndroidManifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ec4d8efd183..0c33b042b80 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -141,7 +141,8 @@ + android:launchMode="singleTask" + android:configChanges="orientation|screenSize|keyboardHidden"> From 11d3b19c513b3bef0afa197856626737cd268fbe Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Fri, 10 Dec 2021 22:37:22 +0800 Subject: [PATCH 12/12] Fix the highlight error after unfolding the device Unfolding a device generates mutiple lifecycle events and resets the highlight key. Check if there's only one activity in the task and then perform the reset. Fix: 209016927 Test: manual Change-Id: I49988fa913270d35a04436777433b7669afb72df --- .../homepage/SettingsHomepageActivity.java | 2 +- .../settings/homepage/TopLevelSettings.java | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index 9076053ff65..183a2fbf5f5 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -311,7 +311,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements } private void launchDeepLinkIntentToRight() { - if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) { + if (!mIsEmbeddingActivityEnabled) { return; } diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java index f2e5a3528f7..f76a3de85d7 100644 --- a/src/com/android/settings/homepage/TopLevelSettings.java +++ b/src/com/android/settings/homepage/TopLevelSettings.java @@ -19,12 +19,14 @@ package com.android.settings.homepage; import static com.android.settings.search.actionbar.SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR; import static com.android.settingslib.search.SearchIndexable.MOBILE; +import android.app.ActivityManager; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import androidx.fragment.app.Fragment; import androidx.preference.Preference; @@ -53,6 +55,7 @@ public class TopLevelSettings extends DashboardFragment implements private static final String SAVED_HIGHLIGHT_MIXIN = "highlight_mixin"; private static final String PREF_KEY_SUPPORT = "top_level_support"; + private boolean mIsEmbeddingActivityEnabled; private TopLevelHighlightMixin mHighlightMixin; private boolean mFirstStarted = true; @@ -122,7 +125,9 @@ public class TopLevelSettings extends DashboardFragment implements @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext())) { + mIsEmbeddingActivityEnabled = + ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext()); + if (!mIsEmbeddingActivityEnabled) { return; } @@ -138,15 +143,23 @@ public class TopLevelSettings extends DashboardFragment implements public void onStart() { if (mFirstStarted) { mFirstStarted = false; - } else if (!ActivityEmbeddingUtils.isTwoPaneResolution(getActivity())) { + } else if (mIsEmbeddingActivityEnabled && isOnlyOneActivityInTask() + && !ActivityEmbeddingUtils.isTwoPaneResolution(getActivity())) { // Set default highlight menu key for 1-pane homepage since it will show the placeholder // page once changing back to 2-pane. + Log.i(TAG, "Set default menu key"); setHighlightMenuKey(getString(SettingsHomepageActivity.DEFAULT_HIGHLIGHT_MENU_KEY), /* scrollNeeded= */ false); } super.onStart(); } + private boolean isOnlyOneActivityInTask() { + final ActivityManager.RunningTaskInfo taskInfo = getSystemService(ActivityManager.class) + .getRunningTasks(1).get(0); + return taskInfo.numActivities == 1; + } + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -225,8 +238,7 @@ public class TopLevelSettings extends DashboardFragment implements @Override protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { - if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(getContext()) - || !(getActivity() instanceof SettingsHomepageActivity)) { + if (!mIsEmbeddingActivityEnabled || !(getActivity() instanceof SettingsHomepageActivity)) { return super.onCreateAdapter(preferenceScreen); } return mHighlightMixin.onCreateAdapter(this, preferenceScreen);