From a99b8799eeaa5e151c84ef10cbcb360f4a6b3646 Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Mon, 15 Jun 2020 18:57:15 +0800 Subject: [PATCH 1/9] Make Telephony related PreferenceController un-sliceable Since the structure of Telephony is changed, some of Telephony related PreferenceController cannot be converted to slices smoothly whether devices have mobile data or not. So we decided to make them un-sliceable. Bug: 155846002 Test: robotests, manual 1. Settings -> Settings search -> search for roaming 2. Check that roaming result has no toggle widget. Change-Id: I7cba8f6a750d9780a59411942665f68dac61248c --- .../TelephonyTogglePreferenceController.java | 5 ++ ...lephonyTogglePreferenceControllerTest.java | 67 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/robotests/src/com/android/settings/network/telephony/TelephonyTogglePreferenceControllerTest.java diff --git a/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java b/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java index 84aa0cb065a..c9ce9b04b76 100644 --- a/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java +++ b/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java @@ -59,6 +59,11 @@ public abstract class TelephonyTogglePreferenceController extends TogglePreferen mSetSessionCount.getAndDecrement(); } + @Override + public boolean isSliceable() { + return false; + } + /** * Get carrier config based on specific subscription id. * diff --git a/tests/robotests/src/com/android/settings/network/telephony/TelephonyTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/TelephonyTogglePreferenceControllerTest.java new file mode 100644 index 00000000000..75425123c4b --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/telephony/TelephonyTogglePreferenceControllerTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.network.telephony; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class TelephonyTogglePreferenceControllerTest { + + private Context mContext; + private FakeTelephonyToggle mFakeTelephonyToggle; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mFakeTelephonyToggle = new FakeTelephonyToggle(mContext, "key"); + } + + @Test + public void isSliceable_byDefault_shouldReturnFalse() { + assertThat(mFakeTelephonyToggle.isSliceable()).isFalse(); + } + + private static class FakeTelephonyToggle extends TelephonyTogglePreferenceController { + + private FakeTelephonyToggle(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public boolean isChecked() { + return false; + } + + @Override + public boolean setChecked(boolean isChecked) { + return false; + } + + @Override + public int getAvailabilityStatus(int subId) { + return 0; + } + } +} From 3ee13ce4d6533831c67a76b17e1dd2352e083c08 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Thu, 18 Jun 2020 03:03:35 +0800 Subject: [PATCH 2/9] Fix homepage cards show up problem after first launch ContextualCardLoader was waiting for eligible card checkers with a 300 ms timeout one by one in a loop. So if more than four checkers expire, the total waiting time will exceed the 1 sec homepage content loading timeout, which causes no card shows up. The solution is to leverage all the checkers' timeout starting time, and increase the timeout for all checkers. So that ContextualCardLoader just needs to wait for all checkers for at most 400 ms and then can get the results. Bug: 159236069 Test: robotest Change-Id: I601ac4151bf8be68b30eaabdb45a4e1ace95653f --- .../contextualcards/ContextualCardLoader.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java index 4d1a1d476bf..d30fe59a51d 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java @@ -38,14 +38,14 @@ import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUt import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.AsyncLoaderCompat; -import com.android.settingslib.utils.ThreadUtils; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; public class ContextualCardLoader extends AsyncLoaderCompat> { @@ -56,7 +56,7 @@ public class ContextualCardLoader extends AsyncLoaderCompat static final int CARD_CONTENT_LOADER_ID = 1; private static final String TAG = "ContextualCardLoader"; - private static final long ELIGIBILITY_CHECKER_TIMEOUT_MS = 300; + private static final long ELIGIBILITY_CHECKER_TIMEOUT_MS = 400; private final ContentObserver mObserver = new ContentObserver( new Handler(Looper.getMainLooper())) { @@ -184,23 +184,37 @@ public class ContextualCardLoader extends AsyncLoaderCompat @VisibleForTesting List filterEligibleCards(List candidates) { + final ExecutorService executor = Executors.newFixedThreadPool(candidates.size()); final List cards = new ArrayList<>(); - final List> eligibleCards = new ArrayList<>(); + List> eligibleCards = new ArrayList<>(); - for (ContextualCard card : candidates) { - final EligibleCardChecker checker = new EligibleCardChecker(mContext, card); - eligibleCards.add(ThreadUtils.postOnBackgroundThread(checker)); + final List checkers = candidates.stream() + .map(card -> new EligibleCardChecker(mContext, card)) + .collect(Collectors.toList()); + try { + eligibleCards = executor.invokeAll(checkers, ELIGIBILITY_CHECKER_TIMEOUT_MS, + TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Log.w(TAG, "Failed to get eligible states for all cards", e); } + executor.shutdown(); + // Collect future and eligible cards - for (Future cardFuture : eligibleCards) { + for (int i = 0; i < eligibleCards.size(); i++) { + final Future cardFuture = eligibleCards.get(i); + if (cardFuture.isCancelled()) { + Log.w(TAG, "Timeout getting eligible state for card: " + + candidates.get(i).getSliceUri()); + continue; + } + try { - final ContextualCard card = cardFuture.get(ELIGIBILITY_CHECKER_TIMEOUT_MS, - TimeUnit.MILLISECONDS); + final ContextualCard card = cardFuture.get(); if (card != null) { cards.add(card); } - } catch (ExecutionException | InterruptedException | TimeoutException e) { - Log.w(TAG, "Failed to get eligible state for card: " + e.toString()); + } catch (Exception e) { + Log.w(TAG, "Failed to get eligible state for card", e); } } return cards; From f7e6c1e4c5adb58b0dc7ddf4321fc370e117c999 Mon Sep 17 00:00:00 2001 From: Stanley Wang Date: Thu, 18 Jun 2020 16:19:26 +0800 Subject: [PATCH 3/9] Add log to capture the hardware status. Sometimes Settings Search show the items that are not supported by the hardware. e.g. FaceLock. Add log to check the HW status when the problem occurred. Bug: 156667203 Test: watch the log output. Change-Id: Ie6a89f338aac6f7bdefc69fc84cfa5bf848ed015 --- .../biometrics/face/FaceSettings.java | 28 ++++++++++++++----- .../FaceSettingsAppPreferenceController.java | 8 +++--- ...SettingsAttentionPreferenceController.java | 2 +- ...ceSettingsConfirmPreferenceController.java | 3 +- ...eSettingsKeyguardPreferenceController.java | 4 +-- ...sLockscreenBypassPreferenceController.java | 4 +-- ...tingsRemoveButtonPreferenceController.java | 2 +- .../face/FaceStatusPreferenceController.java | 2 +- ...stGestureSettingsPreferenceController.java | 9 ++++-- 9 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java index a493ae01dc9..04df11a17b1 100644 --- a/src/com/android/settings/biometrics/face/FaceSettings.java +++ b/src/com/android/settings/biometrics/face/FaceSettings.java @@ -95,9 +95,20 @@ public class FaceSettings extends DashboardFragment { private final FaceSettingsEnrollButtonPreferenceController.Listener mEnrollListener = intent -> startActivityForResult(intent, ENROLL_REQUEST); - public static boolean isAvailable(Context context) { + /** + * @param context + * @return true if the Face hardware is detected. + */ + public static boolean isFaceHardwareDetected(Context context) { FaceManager manager = Utils.getFaceManagerOrNull(context); - return manager != null && manager.isHardwareDetected(); + boolean isHardwareDetected = false; + if (manager == null) { + Log.d(TAG, "FaceManager is null"); + } else { + isHardwareDetected = manager.isHardwareDetected(); + Log.d(TAG, "FaceManager is not null. Hardware detected: " + isHardwareDetected); + } + return manager != null && isHardwareDetected; } @Override @@ -126,7 +137,7 @@ public class FaceSettings extends DashboardFragment { super.onCreate(savedInstanceState); final Context context = getPrefContext(); - if (!isAvailable(context)) { + if (!isFaceHardwareDetected(context)) { Log.w(TAG, "no faceManager, finish this"); finish(); return; @@ -273,7 +284,7 @@ public class FaceSettings extends DashboardFragment { @Override protected List createPreferenceControllers(Context context) { - if (!isAvailable(context)) { + if (!isFaceHardwareDetected(context)) { return null; } mControllers = buildPreferenceControllers(context, getSettingsLifecycle()); @@ -314,7 +325,7 @@ public class FaceSettings extends DashboardFragment { @Override public List createPreferenceControllers( Context context) { - if (isAvailable(context)) { + if (isFaceHardwareDetected(context)) { return buildPreferenceControllers(context, null /* lifecycle */); } else { return null; @@ -323,7 +334,7 @@ public class FaceSettings extends DashboardFragment { @Override protected boolean isPageSearchEnabled(Context context) { - if (isAvailable(context)) { + if (isFaceHardwareDetected(context)) { return hasEnrolledBiometrics(context); } @@ -333,7 +344,10 @@ public class FaceSettings extends DashboardFragment { @Override public List getNonIndexableKeys(Context context) { final List keys = super.getNonIndexableKeys(context); - if (isAvailable(context)) { + final boolean isFaceHardwareDetected = isFaceHardwareDetected(context); + Log.d(TAG, "Get non indexable keys. isFaceHardwareDetected: " + + isFaceHardwareDetected + ", size:" + keys.size()); + if (isFaceHardwareDetected) { final boolean hasEnrolled = hasEnrolledBiometrics(context); keys.add(hasEnrolled ? PREF_KEY_ENROLL_FACE_UNLOCK : PREF_KEY_DELETE_FACE_DATA); diff --git a/src/com/android/settings/biometrics/face/FaceSettingsAppPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsAppPreferenceController.java index a54171e8c42..bde146b19cd 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsAppPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsAppPreferenceController.java @@ -22,10 +22,10 @@ import android.content.Context; import android.hardware.face.FaceManager; import android.provider.Settings; -import com.android.settings.Utils; - import androidx.preference.Preference; +import com.android.settings.Utils; + /** * Preference controller for Face settings page controlling the ability to use * Face authentication in apps (through BiometricPrompt). @@ -51,7 +51,7 @@ public class FaceSettingsAppPreferenceController extends FaceSettingsPreferenceC @Override public boolean isChecked() { - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { return false; } return Settings.Secure.getIntForUser( @@ -67,7 +67,7 @@ public class FaceSettingsAppPreferenceController extends FaceSettingsPreferenceC @Override public void updateState(Preference preference) { super.updateState(preference); - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { preference.setEnabled(false); } else if (!mFaceManager.hasEnrolledTemplates(getUserId())) { preference.setEnabled(false); diff --git a/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java index e5fee758be5..200c0b949ec 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java @@ -93,7 +93,7 @@ public class FaceSettingsAttentionPreferenceController extends FaceSettingsPrefe @Override public boolean isChecked() { - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { return true; } // Set to disabled until we know the true value. diff --git a/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java index c65cd23342e..c5157421132 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java @@ -25,7 +25,6 @@ import android.provider.Settings; import androidx.preference.Preference; import com.android.settings.Utils; -import com.android.settings.core.TogglePreferenceController; /** * Preference controller giving the user an option to always require confirmation. @@ -65,7 +64,7 @@ public class FaceSettingsConfirmPreferenceController extends FaceSettingsPrefere @Override public void updateState(Preference preference) { super.updateState(preference); - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { preference.setEnabled(false); } else if (!mFaceManager.hasEnrolledTemplates(getUserId())) { preference.setEnabled(false); diff --git a/src/com/android/settings/biometrics/face/FaceSettingsKeyguardPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsKeyguardPreferenceController.java index 7e7a748faec..8ee7ffd57ce 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsKeyguardPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsKeyguardPreferenceController.java @@ -53,7 +53,7 @@ public class FaceSettingsKeyguardPreferenceController extends FaceSettingsPrefer @Override public boolean isChecked() { - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { return false; } else if (getRestrictingAdmin() != null) { return false; @@ -77,7 +77,7 @@ public class FaceSettingsKeyguardPreferenceController extends FaceSettingsPrefer public void updateState(Preference preference) { EnforcedAdmin admin; super.updateState(preference); - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { preference.setEnabled(false); } else if ((admin = getRestrictingAdmin()) != null) { ((RestrictedSwitchPreference) preference).setDisabledByAdmin(admin); diff --git a/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java index 5c46a3d9cc9..0a2757b5cbd 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java @@ -47,7 +47,7 @@ public class FaceSettingsLockscreenBypassPreferenceController @Override public boolean isChecked() { - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { return false; } else if (getRestrictingAdmin() != null) { return false; @@ -69,7 +69,7 @@ public class FaceSettingsLockscreenBypassPreferenceController public void updateState(Preference preference) { EnforcedAdmin admin; super.updateState(preference); - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { preference.setEnabled(false); } else if ((admin = getRestrictingAdmin()) != null) { ((RestrictedSwitchPreference) preference).setDisabledByAdmin(admin); diff --git a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java index a4da7597929..6c9806043d4 100644 --- a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java @@ -165,7 +165,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference .findViewById(R.id.security_settings_face_settings_remove_button); mButton.setOnClickListener(this); - if (!FaceSettings.isAvailable(mContext)) { + if (!FaceSettings.isFaceHardwareDetected(mContext)) { mButton.setEnabled(false); } else { mButton.setEnabled(!mRemoving); diff --git a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java index cb82b5e8563..648da9b6ef7 100644 --- a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java +++ b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java @@ -41,7 +41,7 @@ public class FaceStatusPreferenceController extends BiometricStatusPreferenceCon @Override protected boolean isDeviceSupported() { - return mFaceManager != null && mFaceManager.isHardwareDetected(); + return FaceSettings.isFaceHardwareDetected(mContext); } @Override diff --git a/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java b/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java index 8e7031d9cee..49a9de62827 100644 --- a/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java +++ b/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java @@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENA import android.content.Context; import android.provider.Settings; +import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -31,6 +32,7 @@ import com.android.settings.overlay.FeatureFactory; public class AssistGestureSettingsPreferenceController extends GesturePreferenceController { + private static final String TAG = "AssistGesture"; private static final String PREF_KEY_VIDEO = "gesture_assist_video"; private static final String SECURE_KEY_ASSIST = ASSIST_GESTURE_ENABLED; @@ -55,8 +57,11 @@ public class AssistGestureSettingsPreferenceController extends GesturePreference @Override public int getAvailabilityStatus() { - final boolean isAvailable = mAssistOnly ? mFeatureProvider.isSupported(mContext) - : mFeatureProvider.isSensorAvailable(mContext); + final boolean isSupported = mFeatureProvider.isSupported(mContext); + final boolean isSensorAvailable = mFeatureProvider.isSensorAvailable(mContext); + final boolean isAvailable = mAssistOnly ? isSupported : isSensorAvailable; + Log.d(TAG, "mAssistOnly:" + mAssistOnly + ", isSupported:" + isSupported + + ", isSensorAvailable:" + isSensorAvailable); return isAvailable ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } From 40b79387cc3f9124068830185f334c263c79dce9 Mon Sep 17 00:00:00 2001 From: alexylli Date: Thu, 18 Jun 2020 14:24:55 +0800 Subject: [PATCH 4/9] [Settings] Avoids disabled component when page start from setup wizard. Fixs when disable component during system busy, the framework have high possibility to force closing activities on the same task. test 1. setup a pin code and go through to the anything else page. click "control info on lock screen". >> The SetupRedactionInterstitial not been disabled. >> The lock screen can be setup repeatedly. 2. into home screen. using adb command to start SetupRedactionInterstitial. >> the screen not shown. 3. using adb command to start RedactionInterstitial and click "done". >> the activity existed normally. Bug: 158961310 Test: Manual Change-Id: I09fe95db591d3d3603b2c13623a0873bfab3bac0 --- .../android/settings/SetupRedactionInterstitial.java | 12 ++++++++++++ .../settings/notification/RedactionInterstitial.java | 7 ++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/SetupRedactionInterstitial.java b/src/com/android/settings/SetupRedactionInterstitial.java index 90f6c21dab7..4747d84cf82 100644 --- a/src/com/android/settings/SetupRedactionInterstitial.java +++ b/src/com/android/settings/SetupRedactionInterstitial.java @@ -20,9 +20,12 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.os.Bundle; import com.android.settings.notification.RedactionInterstitial; +import com.google.android.setupcompat.util.WizardManagerHelper; + /** * Setup Wizard's version of RedactionInterstitial screen. It inherits the logic and basic structure * from RedactionInterstitial class, and should remain similar to that behaviorally. This class @@ -46,6 +49,15 @@ public class SetupRedactionInterstitial extends RedactionInterstitial { PackageManager.DONT_KILL_APP); } + @Override + protected void onCreate(Bundle savedInstance) { + // Only allow to start the activity from Setup Wizard. + if (!WizardManagerHelper.isAnySetupWizard(getIntent())) { + finish(); + } + super.onCreate(savedInstance); + } + @Override public Intent getIntent() { Intent modIntent = new Intent(super.getIntent()); diff --git a/src/com/android/settings/notification/RedactionInterstitial.java b/src/com/android/settings/notification/RedactionInterstitial.java index 14f99f38b3d..ac648a51e62 100644 --- a/src/com/android/settings/notification/RedactionInterstitial.java +++ b/src/com/android/settings/notification/RedactionInterstitial.java @@ -48,6 +48,7 @@ import com.android.settingslib.RestrictedLockUtilsInternal; import com.google.android.setupcompat.template.FooterBarMixin; import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupcompat.util.WizardManagerHelper; import com.google.android.setupdesign.GlifLayout; public class RedactionInterstitial extends SettingsActivity { @@ -144,7 +145,11 @@ public class RedactionInterstitial extends SettingsActivity { } private void onDoneButtonClicked(View view) { - SetupRedactionInterstitial.setEnabled(getContext(), false); + // If the activity starts by Setup Wizard, then skip disable component which avoids the + // framework force closing all activities on the same task when the system is busy. + if (!WizardManagerHelper.isAnySetupWizard(getIntent())) { + SetupRedactionInterstitial.setEnabled(getContext(), false); + } final RedactionInterstitial activity = (RedactionInterstitial) getActivity(); if (activity != null) { activity.setResult(RESULT_OK, null); From 2e0758f7ae8f54fc5c23792d26e7d4f39569e43b Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Sun, 21 Jun 2020 21:34:31 +0800 Subject: [PATCH 5/9] Decrease memory usage for sleep idle test Postpone setTheme to decrease memory usage of Settings for the test android.platform.test.scenario.sleep.Idle Bug: 156631776 Test: forrest test Change-Id: I4178eae0832e15c9d6da4a0f1f2898f5c797d388 --- .../android/settings/slices/SettingsSliceProvider.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index 75061a5dac5..c22d001a2d8 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -143,7 +143,7 @@ public class SettingsSliceProvider extends SliceProvider { @VisibleForTesting final Map mPinnedWorkers = new ArrayMap<>(); - private boolean mNightMode; + private Boolean mNightMode; public SettingsSliceProvider() { super(READ_SEARCH_INDEXABLES); @@ -153,8 +153,6 @@ public class SettingsSliceProvider extends SliceProvider { public boolean onCreateSliceProvider() { mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext()); mSliceWeakDataCache = new WeakHashMap<>(); - mNightMode = Utils.isNightMode(getContext()); - getContext().setTheme(R.style.Theme_SettingsBase); return true; } @@ -207,7 +205,10 @@ public class SettingsSliceProvider extends SliceProvider { } final boolean nightMode = Utils.isNightMode(getContext()); - if (mNightMode != nightMode) { + if (mNightMode == null) { + mNightMode = nightMode; + getContext().setTheme(R.style.Theme_SettingsBase); + } else if (mNightMode != nightMode) { Log.d(TAG, "Night mode changed, reload theme"); mNightMode = nightMode; getContext().getTheme().rebase(); From b54f9b4921547ea931ad1c939a6220279daa34c8 Mon Sep 17 00:00:00 2001 From: Yi-Ling Chuang Date: Mon, 22 Jun 2020 14:53:10 +0800 Subject: [PATCH 6/9] Disable the defulat animation of the RecyclerView to avoid UI jank. When users go to a sub page in Settings and come back to the homepage again, those sticky cards flicker which makes the UI janky. This is because we rebind sticky cards to ensure it's always up-to-date, where the recyclerView has its default animation while notifyItemInserted(), ane thus introduce the jank. Fixes: 158627602 Test: Open Settings -> go to sub pages -> back to the homepage -> cards are not flickering. Change-Id: I2104dbe0bed8b2486c35521bcc0b5c8b54efb995 --- .../homepage/contextualcards/ContextualCardsFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java index ebfaca9544e..9f0023aa15c 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsFragment.java @@ -109,6 +109,7 @@ public class ContextualCardsFragment extends InstrumentedFragment implements mCardsContainer.setLayoutManager(mLayoutManager); mContextualCardsAdapter = new ContextualCardsAdapter(context, this /* lifecycleOwner */, mContextualCardManager); + mCardsContainer.setItemAnimator(null); mCardsContainer.setAdapter(mContextualCardsAdapter); mContextualCardManager.setListener(mContextualCardsAdapter); mCardsContainer.setListener(this); From 73afb042dc3e6359f0d3350bc25038c5f26d0826 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Sun, 21 Jun 2020 17:06:53 +0800 Subject: [PATCH 7/9] Workaround of Slice not pinned bug when using SliceCallback When a client registers a SliceCallback, it starts to observe Slice changes. The observer is running in the main looper and binds Slice in AsyncTask. However, when the client unregisters the callback, it unpins the Slice in the client's thread. Thus, if the AsyncTask has pinned the slice but hasn't bound it yet, it may be unpinned by the client, and an exception occurs. This fix is to unpin the Slice in the same SerialExecutor of AsyncTask, which is to ensure bindSlice() and unpinSlice() are called in the same thread one after another without the race condition. Bug: 157387583 Test: robotest Change-Id: If5f4b4bc0d8c5a2800cad8ff2afd7084426a6c96 --- .../homepage/contextualcards/EligibleCardChecker.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java index 43403306cb3..46b4c869ecb 100644 --- a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java +++ b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java @@ -22,6 +22,7 @@ import android.app.settings.SettingsEnums; import android.content.ContentResolver; import android.content.Context; import android.net.Uri; +import android.os.AsyncTask; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -32,6 +33,7 @@ import androidx.slice.core.SliceAction; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.settingslib.utils.ThreadUtils; import java.util.List; import java.util.concurrent.Callable; @@ -115,7 +117,12 @@ public class EligibleCardChecker implements Callable { // Register a trivial callback to pin the slice manager.registerSliceCallback(uri, callback); final Slice slice = manager.bindSlice(uri); - manager.unregisterSliceCallback(uri, callback); + + // Workaround of unpinning slice in the same SerialExecutor of AsyncTask as SliceCallback's + // observer. + ThreadUtils.postOnMainThread(() -> + AsyncTask.execute(() -> manager.unregisterSliceCallback(uri, callback)) + ); return slice; } From f1a0801c00e57407a8d7eacdac72905cdbe0ceee Mon Sep 17 00:00:00 2001 From: Stanley Wang Date: Mon, 22 Jun 2020 14:38:37 +0800 Subject: [PATCH 8/9] Fix the bug of not showing lock screen before entering Smart Lock page. Controller can't find the target preference to handle the click event. Store the preference keys to match the clicked item. Fixes: 158716163 Test: run robotest and manually test the click behavior Change-Id: Ie243206ceffef013c56c4ea29c14fe56da510fb6 --- .../TrustAgentListPreferenceController.java | 10 +++++-- ...rustAgentListPreferenceControllerTest.java | 27 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java b/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java index 6067b77e59f..19e25fc5882 100644 --- a/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java +++ b/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; -import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -46,6 +45,7 @@ import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; import com.android.settingslib.search.SearchIndexableRaw; +import java.util.ArrayList; import java.util.List; public class TrustAgentListPreferenceController extends AbstractPreferenceController @@ -66,6 +66,9 @@ public class TrustAgentListPreferenceController extends AbstractPreferenceContro private Intent mTrustAgentClickIntent; private PreferenceCategory mSecurityCategory; + @VisibleForTesting + final List mTrustAgentsKeyList; + public TrustAgentListPreferenceController(Context context, SecuritySettings host, Lifecycle lifecycle) { super(context); @@ -74,6 +77,7 @@ public class TrustAgentListPreferenceController extends AbstractPreferenceContro mHost = host; mLockPatternUtils = provider.getLockPatternUtils(context); mTrustAgentManager = provider.getTrustAgentManager(); + mTrustAgentsKeyList = new ArrayList(); if (lifecycle != null) { lifecycle.addObserver(this); } @@ -113,7 +117,7 @@ public class TrustAgentListPreferenceController extends AbstractPreferenceContro @Override public boolean handlePreferenceTreeClick(Preference preference) { - if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { + if (!mTrustAgentsKeyList.contains(preference.getKey())) { return super.handlePreferenceTreeClick(preference); } final ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper( @@ -189,6 +193,7 @@ public class TrustAgentListPreferenceController extends AbstractPreferenceContro mSecurityCategory.removePreference(oldAgent); } } + mTrustAgentsKeyList.clear(); // Then add new ones. final boolean hasSecurity = mLockPatternUtils.isSecure(MY_USER_ID); @@ -196,6 +201,7 @@ public class TrustAgentListPreferenceController extends AbstractPreferenceContro final RestrictedPreference trustAgentPreference = new RestrictedPreference(mSecurityCategory.getContext()); TrustAgentManager.TrustAgentComponentInfo agent = agents.get(i); + mTrustAgentsKeyList.add(PREF_KEY_TRUST_AGENT + i); trustAgentPreference.setKey(PREF_KEY_TRUST_AGENT + i); trustAgentPreference.setTitle(agent.title); trustAgentPreference.setSummary(agent.summary); diff --git a/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java index c0fbff79358..0463e003ae2 100644 --- a/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java @@ -16,10 +16,8 @@ package com.android.settings.security.trustagent; -import static com.android.settings.security.trustagent.TrustAgentListPreferenceController - .PREF_KEY_SECURITY_CATEGORY; -import static com.android.settings.security.trustagent.TrustAgentListPreferenceController - .PREF_KEY_TRUST_AGENT; +import static com.android.settings.security.trustagent.TrustAgentListPreferenceController.PREF_KEY_SECURITY_CATEGORY; +import static com.android.settings.security.trustagent.TrustAgentListPreferenceController.PREF_KEY_TRUST_AGENT; import static com.google.common.truth.Truth.assertThat; @@ -172,6 +170,26 @@ public class TrustAgentListPreferenceControllerTest { verify(mCategory, never()).addPreference(any(Preference.class)); } + @Test + public void onResume_controllerShouldHasKey() { + final List agents = new ArrayList<>(); + final TrustAgentManager.TrustAgentComponentInfo agent = + mock(TrustAgentManager.TrustAgentComponentInfo.class); + agent.title = "Test_title"; + agent.summary = "test summary"; + agent.componentName = new ComponentName("pkg", "agent"); + agent.admin = null; + agents.add(agent); + when(mTrustAgentManager.getActiveTrustAgents(mActivity, mLockPatternUtils)) + .thenReturn(agents); + final String key = PREF_KEY_TRUST_AGENT + 0; + + mController.displayPreference(mScreen); + mController.onResume(); + + assertThat(mController.mTrustAgentsKeyList).containsExactly(key); + } + @Test public void updateDynamicRawDataToIndex_shouldIndexAgents() { final List agents = new ArrayList<>(); @@ -190,5 +208,4 @@ public class TrustAgentListPreferenceControllerTest { assertThat(indexRaws).hasSize(1); } - } From f3afef8419de2910b03c3670ca25e63ac3c08407 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 14 May 2020 02:03:14 +0200 Subject: [PATCH 9/9] Prevent overlay drawing on top of Bluetooth pairing dialog Bug: 155648639 Change-Id: I99643ee9084f3a9bc1ad9a459ac27c587d832c91 Merged-In: I99643ee9084f3a9bc1ad9a459ac27c587d832c91 --- .../android/settings/bluetooth/BluetoothPairingDialog.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java index 22cb3a683ec..316f541f13f 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java @@ -26,6 +26,8 @@ import android.content.IntentFilter; import android.os.Bundle; import android.support.annotation.VisibleForTesting; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + /** * BluetoothPairingDialog asks the user to enter a PIN / Passkey / simple confirmation * for pairing with a remote Bluetooth device. It is an activity that appears as a dialog. @@ -63,6 +65,8 @@ public class BluetoothPairingDialog extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); Intent intent = getIntent(); mBluetoothPairingController = new BluetoothPairingController(intent, this); // build the dialog fragment