From 94253d725e87ed39fc9fc4dddfcb0060b40850b8 Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Tue, 17 Aug 2021 09:46:00 +0800 Subject: [PATCH 1/3] [Settings] Avoid from floating overlay when displaying SIM selection UI Avoid from floating overlay when displaying SIM selection UI, which could avoid from security issue. Bug: 195667589 Test: local Change-Id: I8db76005f2c3ad6cc94d68703b2b1647d81371f5 (cherry picked from commit 1cc61672ef9ea78f79cfdb55f4e732bd50b06e34) --- src/com/android/settings/sim/SimDialogActivity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java index 252b6c06e25..e5457ae3459 100644 --- a/src/com/android/settings/sim/SimDialogActivity.java +++ b/src/com/android/settings/sim/SimDialogActivity.java @@ -24,6 +24,7 @@ import android.telecom.TelecomManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; +import android.view.WindowManager; import android.widget.Toast; import androidx.fragment.app.Fragment; @@ -59,6 +60,8 @@ public class SimDialogActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().addSystemFlags( + WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); showOrUpdateDialog(); } From a4a806e4ea36019115988672374e79488a82dbee Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Wed, 11 Aug 2021 18:34:07 +0800 Subject: [PATCH 2/3] [Settings] Avoid from immediate update when UI inactive and SIM absent When SIM absent during UI inactive, update can be postponed under the condition of Activity onPause() or onStop(). And update when onResume() or onStart(). This is not simply an improvement on performance but also a better user experience. User might unplug a SIM and plug-in again, and there're cases where a SIM status might change into absent and back to active again due to some mode switch in RIL/modem. From user perspective, the SIM status remain the same and update of content is expected instead of closing the UI and re-enter. Bug: 195631787 Test: local Change-Id: I8248e59895631dc90cf3831398e387b93483280c (cherry picked from commit b2a6266fa91d66b84a540fc8eee891a83fda5538) --- .../telephony/MobileNetworkActivity.java | 158 +++++++++++++----- 1 file changed, 112 insertions(+), 46 deletions(-) diff --git a/src/com/android/settings/network/telephony/MobileNetworkActivity.java b/src/com/android/settings/network/telephony/MobileNetworkActivity.java index c6fe39c2a2b..efb5f8cc08e 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkActivity.java +++ b/src/com/android/settings/network/telephony/MobileNetworkActivity.java @@ -39,7 +39,6 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.Lifecycle; -import com.android.internal.util.CollectionUtils; import com.android.settings.R; import com.android.settings.core.SettingsBaseActivity; import com.android.settings.network.ProxySubscriptionManager; @@ -48,6 +47,7 @@ import com.android.settings.network.helper.SelectableSubscriptions; import com.android.settings.network.helper.SubscriptionAnnotation; import java.util.List; +import java.util.function.Function; /** * Activity for displaying MobileNetworkSettings @@ -64,15 +64,14 @@ public class MobileNetworkActivity extends SettingsBaseActivity @VisibleForTesting ProxySubscriptionManager mProxySubscriptionMgr; - private int mCurSubscriptionId; + private int mCurSubscriptionId = SUB_ID_NULL; // This flag forces subscription information fragment to be re-created. // Otherwise, fragment will be kept when subscription id has not been changed. // // Set initial value to true allows subscription information fragment to be re-created when // Activity re-create occur. - private boolean mFragmentForceReload = true; - private boolean mPendingSubscriptionChange = false; + private boolean mPendingSubscriptionChange = true; @Override protected void onNewIntent(Intent intent) { @@ -80,21 +79,25 @@ public class MobileNetworkActivity extends SettingsBaseActivity validate(intent); setIntent(intent); - int updateSubscriptionIndex = SUB_ID_NULL; + int updateSubscriptionIndex = mCurSubscriptionId; if (intent != null) { updateSubscriptionIndex = intent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL); } + SubscriptionInfo info = getSubscriptionOrDefault(updateSubscriptionIndex); + if (info == null) { + Log.d(TAG, "Invalid subId request " + mCurSubscriptionId + + " -> " + updateSubscriptionIndex); + return; + } + int oldSubId = mCurSubscriptionId; - mCurSubscriptionId = updateSubscriptionIndex; - mFragmentForceReload = (mCurSubscriptionId == oldSubId); - final SubscriptionInfo info = getSubscription(); updateSubscriptions(info, null); // If the subscription has changed or the new intent doesnt contain the opt in action, // remove the old discovery dialog. If the activity is being recreated, we will see // onCreate -> onNewIntent, so the dialog will first be recreated for the old subscription // and then removed. - if (updateSubscriptionIndex != oldSubId || !doesIntentContainOptInAction(intent)) { + if (mCurSubscriptionId != oldSubId || !doesIntentContainOptInAction(intent)) { removeContactDiscoveryDialog(oldSubId); } // evaluate showing the new discovery dialog if this intent contains an action to show the @@ -135,7 +138,13 @@ public class MobileNetworkActivity extends SettingsBaseActivity // perform registration after mCurSubscriptionId been configured. registerActiveSubscriptionsListener(); - final SubscriptionInfo subscription = getSubscription(); + SubscriptionInfo subscription = getSubscriptionOrDefault(mCurSubscriptionId); + if (subscription == null) { + Log.d(TAG, "Invalid subId request " + mCurSubscriptionId); + tryToFinishActivity(); + return; + } + maybeShowContactDiscoveryDialog(subscription); updateSubscriptions(subscription, null); @@ -158,39 +167,81 @@ public class MobileNetworkActivity extends SettingsBaseActivity * Implementation of ProxySubscriptionManager.OnActiveSubscriptionChangedListener */ public void onChanged() { + mPendingSubscriptionChange = false; + + if (mCurSubscriptionId == SUB_ID_NULL) { + return; + } + if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { mPendingSubscriptionChange = true; return; } - SubscriptionInfo info = getSubscription(); - int oldSubIndex = mCurSubscriptionId; - updateSubscriptions(info, null); - // Remove the dialog if the subscription associated with this activity changes. - if (info == null) { - // Close the activity when subscription removed - if ((oldSubIndex != SUB_ID_NULL) - && (!isFinishing()) && (!isDestroyed())) { - finish(); + SubscriptionInfo subInfo = getSubscription(mCurSubscriptionId, null); + if (subInfo != null) { + if (mCurSubscriptionId != subInfo.getSubscriptionId()) { + // update based on subscription status change + removeContactDiscoveryDialog(mCurSubscriptionId); + updateSubscriptions(subInfo, null); } return; } - int subIndex = info.getSubscriptionId(); - if (subIndex != oldSubIndex) { - removeContactDiscoveryDialog(oldSubIndex); + + Log.w(TAG, "subId missing: " + mCurSubscriptionId); + + // When UI is not the active one, avoid from destroy it immediately + // but wait until onResume() to see if subscription back online again. + // This is to avoid from glitch behavior of subscription which changes + // the UI when UI is considered as in the background or only partly + // visible. + if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) { + mPendingSubscriptionChange = true; + return; + } + + // Subscription could be missing + tryToFinishActivity(); + } + + protected void runSubscriptionUpdate(Runnable onUpdateRemaining) { + SubscriptionInfo subInfo = getSubscription(mCurSubscriptionId, null); + if (subInfo == null) { + tryToFinishActivity(); + return; + } + if (mCurSubscriptionId != subInfo.getSubscriptionId()) { + removeContactDiscoveryDialog(mCurSubscriptionId); + updateSubscriptions(subInfo, null); + } + onUpdateRemaining.run(); + } + + protected void tryToFinishActivity() { + if ((!isFinishing()) && (!isDestroyed())) { + finish(); } } @Override protected void onStart() { getProxySubscriptionManager().setLifecycle(getLifecycle()); - super.onStart(); - // updateSubscriptions doesn't need to be called, onChanged will always be called after we - // register a listener. if (mPendingSubscriptionChange) { mPendingSubscriptionChange = false; - onChanged(); + runSubscriptionUpdate(() -> super.onStart()); + return; } + super.onStart(); + } + + @Override + protected void onResume() { + if (mPendingSubscriptionChange) { + mPendingSubscriptionChange = false; + runSubscriptionUpdate(() -> super.onResume()); + return; + } + super.onResume(); } @Override @@ -235,30 +286,49 @@ public class MobileNetworkActivity extends SettingsBaseActivity } mCurSubscriptionId = subscriptionIndex; - mFragmentForceReload = false; + } + + /** + * Select one of the subscription as the default subscription. + * @param subAnnoList a list of {@link SubscriptionAnnotation} + * @return ideally the {@link SubscriptionAnnotation} as expected + */ + protected SubscriptionAnnotation defaultSubscriptionSelection( + List subAnnoList) { + return (subAnnoList == null) ? null : + subAnnoList.stream() + .filter(SubscriptionAnnotation::isDisplayAllowed) + .filter(SubscriptionAnnotation::isActive) + .findFirst().orElse(null); + } + + protected SubscriptionInfo getSubscriptionOrDefault(int subscriptionId) { + return getSubscription(subscriptionId, + (subscriptionId != SUB_ID_NULL) ? null : ( + subAnnoList -> defaultSubscriptionSelection(subAnnoList) + )); } /** * Get the current subscription to display. First check whether intent has {@link - * Settings#EXTRA_SUB_ID} and if so find the subscription with that id. If not, just return the - * first one in the mSubscriptionInfos list since it is already sorted by sim slot. + * Settings#EXTRA_SUB_ID} and if so find the subscription with that id. + * If not, select default one based on {@link Function} provided. + * + * @param preferredSubscriptionId preferred subscription id + * @param selectionOfDefault when true current subscription is absent */ @VisibleForTesting - SubscriptionInfo getSubscription() { + protected SubscriptionInfo getSubscription(int preferredSubscriptionId, + Function, SubscriptionAnnotation> selectionOfDefault) { List subList = (new SelectableSubscriptions(this, true)).call(); - SubscriptionAnnotation currentSubInfo = null; - if (mCurSubscriptionId != SUB_ID_NULL) { - currentSubInfo = subList.stream() - .filter(SubscriptionAnnotation::isDisplayAllowed) - .filter(subAnno -> (subAnno.getSubscriptionId() == mCurSubscriptionId)) - .findFirst().orElse(null); - } - if (currentSubInfo == null) { - currentSubInfo = subList.stream() - .filter(SubscriptionAnnotation::isDisplayAllowed) - .filter(SubscriptionAnnotation::isActive) - .findFirst().orElse(null); + Log.d(TAG, "get subId=" + preferredSubscriptionId + " from " + subList); + SubscriptionAnnotation currentSubInfo = subList.stream() + .filter(SubscriptionAnnotation::isDisplayAllowed) + .filter(subAnno -> (subAnno.getSubscriptionId() == preferredSubscriptionId)) + .findFirst().orElse(null); + if ((currentSubInfo == null) && (selectionOfDefault != null)) { + currentSubInfo = selectionOfDefault.apply(subList); } return (currentSubInfo == null) ? null : currentSubInfo.getSubInfo(); } @@ -285,10 +355,6 @@ public class MobileNetworkActivity extends SettingsBaseActivity final String fragmentTag = buildFragmentTag(subId); if (fragmentManager.findFragmentByTag(fragmentTag) != null) { - if (!mFragmentForceReload) { - Log.d(TAG, "Keep current fragment: " + fragmentTag); - return; - } Log.d(TAG, "Construct fragment: " + fragmentTag); } From 39ebcd090b07cc472e1fff3251db50527558e70d Mon Sep 17 00:00:00 2001 From: Jason Chang Date: Thu, 15 Jul 2021 19:34:10 +0800 Subject: [PATCH 3/3] Fix hold volume keys don't trigger the one handed mode in One-handed mode settings page Add to support observing A11y's Shortcut Hardware provider key ACCESSIBILITY_SHORTCUT_TARGET_SERVICE Bug: 193411296 Test: manual verified on System > Gestures > One-handed mode shortcut Test: make RunSettingsRoboTests ROBOTEST_FILTER= "com.android.settings.gestures .OneHandedEnablePreferenceControllerTest" Test: make RunSettingsRoboTests ROBOTEST_FILTER= "com.android.settings.gestures .OneHandedMainSwitchPreferenceControllerTest" Test: make RunSettingsRoboTests ROBOTEST_FILTER= "com.android.settings.gestures .OneHandedActionPullDownPrefControllerTest" Test: make RunSettingsRoboTests ROBOTEST_FILTER= "com.android.settings.gestures .OneHandedActionShowNotificationPrefControllerTest" Test: make RunSettingsRoboTests ROBOTEST_FILTER= "com.android.settings.gestures .OneHandedPreferenceCategoryControllerTest" Change-Id: I4704a56873f14e52db8f7c0468eed17f190c5dce Merged-Id: I4704a56873f14e52db8f7c0468eed17f190c5dce --- ...OneHandedActionPullDownPrefController.java | 3 ++- ...dActionShowNotificationPrefController.java | 3 ++- .../gestures/OneHandedSettingsUtils.java | 23 +++++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java b/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java index e17165b1856..26b37630d2b 100644 --- a/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java +++ b/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java @@ -93,7 +93,8 @@ public class OneHandedActionPullDownPrefController extends BasePreferenceControl return; } if (uri.equals(OneHandedSettingsUtils.ONE_HANDED_MODE_ENABLED_URI) - || uri.equals(OneHandedSettingsUtils.SHORTCUT_ENABLED_URI)) { + || uri.equals(OneHandedSettingsUtils.SOFTWARE_SHORTCUT_ENABLED_URI) + || uri.equals(OneHandedSettingsUtils.HARDWARE_SHORTCUT_ENABLED_URI)) { mPreference.setEnabled(OneHandedSettingsUtils.canEnableController(mContext)); } else if (uri.equals(OneHandedSettingsUtils.SHOW_NOTIFICATION_ENABLED_URI)) { updateState(mPreference); diff --git a/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java b/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java index 9f56a14afd6..524c135af5e 100644 --- a/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java +++ b/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java @@ -93,7 +93,8 @@ public class OneHandedActionShowNotificationPrefController extends BasePreferenc return; } if (uri.equals(OneHandedSettingsUtils.ONE_HANDED_MODE_ENABLED_URI) - || uri.equals(OneHandedSettingsUtils.SHORTCUT_ENABLED_URI)) { + || uri.equals(OneHandedSettingsUtils.SOFTWARE_SHORTCUT_ENABLED_URI) + || uri.equals(OneHandedSettingsUtils.HARDWARE_SHORTCUT_ENABLED_URI)) { mPreference.setEnabled(OneHandedSettingsUtils.canEnableController(mContext)); } else if (uri.equals(OneHandedSettingsUtils.SHOW_NOTIFICATION_ENABLED_URI)) { updateState(mPreference); diff --git a/src/com/android/settings/gestures/OneHandedSettingsUtils.java b/src/com/android/settings/gestures/OneHandedSettingsUtils.java index f058689912b..04898dc0219 100644 --- a/src/com/android/settings/gestures/OneHandedSettingsUtils.java +++ b/src/com/android/settings/gestures/OneHandedSettingsUtils.java @@ -27,6 +27,7 @@ import android.os.Looper; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; import androidx.annotation.VisibleForTesting; @@ -45,8 +46,10 @@ public class OneHandedSettingsUtils { Settings.Secure.getUriFor(Settings.Secure.ONE_HANDED_MODE_ENABLED); static final Uri SHOW_NOTIFICATION_ENABLED_URI = Settings.Secure.getUriFor(Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED); - static final Uri SHORTCUT_ENABLED_URI = + static final Uri SOFTWARE_SHORTCUT_ENABLED_URI = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); + static final Uri HARDWARE_SHORTCUT_ENABLED_URI = + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); public enum OneHandedTimeout { NEVER(0), SHORT(4), MEDIUM(8), LONG(12); @@ -238,9 +241,20 @@ public class OneHandedSettingsUtils { * @return true if user enabled one-handed shortcut in settings, false otherwise. */ public static boolean getShortcutEnabled(Context context) { - final String targets = Settings.Secure.getStringForUser(context.getContentResolver(), + // Checks SOFTWARE_SHORTCUT_KEY + final String targetsSW = Settings.Secure.getStringForUser(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, sCurrentUserId); - return targets != null ? targets.contains(ONE_HANDED_MODE_TARGET_NAME) : false; + if (!TextUtils.isEmpty(targetsSW) && targetsSW.contains(ONE_HANDED_MODE_TARGET_NAME)) { + return true; + } + + // Checks HARDWARE_SHORTCUT_KEY + final String targetsHW = Settings.Secure.getStringForUser(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, sCurrentUserId); + if (!TextUtils.isEmpty(targetsHW) && targetsHW.contains(ONE_HANDED_MODE_TARGET_NAME)) { + return true; + } + return false; } /** @@ -285,7 +299,8 @@ public class OneHandedSettingsUtils { final ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(ONE_HANDED_MODE_ENABLED_URI, true, this); resolver.registerContentObserver(SHOW_NOTIFICATION_ENABLED_URI, true, this); - resolver.registerContentObserver(SHORTCUT_ENABLED_URI, true, this); + resolver.registerContentObserver(SOFTWARE_SHORTCUT_ENABLED_URI, true, this); + resolver.registerContentObserver(HARDWARE_SHORTCUT_ENABLED_URI, true, this); } @Override