summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java3
-rw-r--r--cmds/screencap/screencap.cpp39
-rw-r--r--core/java/Android.bp11
-rw-r--r--core/java/android/view/InsetsController.java2
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java13
-rw-r--r--core/java/android/view/inputmethod/TextAppearanceInfo.java5
-rw-r--r--core/java/com/android/internal/protolog/common/ProtoLog.java20
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java83
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml80
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt342
-rw-r--r--packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt138
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt15
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt200
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt27
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt2
-rw-r--r--packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java410
-rw-r--r--packages/SystemUI/proguard_common.flags19
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml9
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml6
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/ids.xml6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt92
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt153
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt107
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt160
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt65
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt)0
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt71
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java196
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java19
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastDeliveryFailedException.java32
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java19
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java3
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueImpl.java8
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java76
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java3
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java23
-rw-r--r--services/core/java/com/android/server/display/BrightnessSetting.java17
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java7
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java11
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java3
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerControllerInterface.java14
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java4
-rw-r--r--services/core/java/com/android/server/display/PersistentDataStore.java71
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java10
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java16
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java64
-rw-r--r--services/core/java/com/android/server/notification/NotificationUsageStats.java60
-rw-r--r--services/core/java/com/android/server/notification/RateEstimator.java15
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java6
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java20
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java6
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java13
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java14
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java110
-rw-r--r--services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java20
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java99
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java94
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java7
-rw-r--r--tests/FlickerTests/Android.bp4
-rw-r--r--tests/FlickerTests/AndroidTestTemplate.xml79
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt)2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt)2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt)2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt)2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt)3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt)4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt)3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt)2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt)3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt)2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt)4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt)3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt)6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt)2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt)3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt)6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt)4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt)4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt)3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt)2
-rw-r--r--tests/InputMethodStressTest/AndroidManifest.xml3
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java37
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java10
-rw-r--r--tools/protologtool/Android.bp2
153 files changed, 2944 insertions, 1493 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 175c8d1cc4f3..07958dd0fef5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -3153,7 +3153,8 @@ public final class QuotaController extends StateController {
private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
private static final long DEFAULT_MIN_QUOTA_CHECK_DELAY_MS = MINUTE_IN_MILLIS;
- private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 45 * MINUTE_IN_MILLIS;
+ // TODO(267949143): set a different limit for headless system apps
+ private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 60 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS;
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index d7222d248911..863efffe3807 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -30,6 +30,8 @@
#include <binder/ProcessState.h>
+#include <ftl/concat.h>
+#include <ftl/optional.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
@@ -45,14 +47,7 @@ using namespace android;
#define COLORSPACE_SRGB 1
#define COLORSPACE_DISPLAY_P3 2
-static void usage(const char* pname, std::optional<PhysicalDisplayId> displayId)
-{
- std::string defaultDisplayStr = "";
- if (!displayId) {
- defaultDisplayStr = "";
- } else {
- defaultDisplayStr = " (default: " + to_string(*displayId) + ")";
- }
+void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
fprintf(stderr,
"usage: %s [-hp] [-d display-id] [FILENAME]\n"
" -h: this message\n"
@@ -61,7 +56,13 @@ static void usage(const char* pname, std::optional<PhysicalDisplayId> displayId)
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"If FILENAME ends with .png it will be saved as a png.\n"
"If FILENAME is not given, the results will be printed to stdout.\n",
- pname, defaultDisplayStr.c_str());
+ pname,
+ displayIdOpt
+ .transform([](DisplayId id) {
+ return std::string(ftl::Concat(" (default: ", id.value, ')').str());
+ })
+ .value_or(std::string())
+ .c_str());
}
static int32_t flinger2bitmapFormat(PixelFormat f)
@@ -132,7 +133,7 @@ int main(int argc, char** argv)
fprintf(stderr, "Failed to get ID for any displays.\n");
return 1;
}
- std::optional<PhysicalDisplayId> displayId;
+ std::optional<DisplayId> displayIdOpt;
const char* pname = argv[0];
bool png = false;
int c;
@@ -142,8 +143,8 @@ int main(int argc, char** argv)
png = true;
break;
case 'd':
- displayId = DisplayId::fromValue<PhysicalDisplayId>(atoll(optarg));
- if (!displayId) {
+ displayIdOpt = DisplayId::fromValue(atoll(optarg));
+ if (!displayIdOpt) {
fprintf(stderr, "Invalid display ID: %s\n", optarg);
return 1;
}
@@ -151,15 +152,15 @@ int main(int argc, char** argv)
case '?':
case 'h':
if (ids.size() == 1) {
- displayId = ids.front();
- }
- usage(pname, displayId);
+ displayIdOpt = ids.front();
+ }
+ usage(pname, displayIdOpt);
return 1;
}
}
- if (!displayId) { // no diplsay id is specified
- displayId = ids.front();
+ if (!displayIdOpt) {
+ displayIdOpt = ids.front();
if (ids.size() > 1) {
fprintf(stderr,
"[Warning] Multiple displays were found, but no display id was specified! "
@@ -191,7 +192,7 @@ int main(int argc, char** argv)
}
if (fd == -1) {
- usage(pname, displayId);
+ usage(pname, displayIdOpt);
return 1;
}
@@ -208,7 +209,7 @@ int main(int argc, char** argv)
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t result = ScreenshotClient::captureDisplay(*displayId, captureListener);
+ status_t result = ScreenshotClient::captureDisplay(*displayIdOpt, captureListener);
if (result != NO_ERROR) {
close(fd);
return 1;
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 02b14ad7a757..f8f4cc3523f2 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -510,6 +510,17 @@ filegroup {
],
}
+// common protolog sources without classes that rely on Android SDK
+filegroup {
+ name: "protolog-common-no-android-src",
+ srcs: [
+ ":protolog-common-src",
+ ],
+ exclude_srcs: [
+ "com/android/internal/protolog/common/ProtoLog.java",
+ ],
+}
+
java_library {
name: "protolog-lib",
platform_apis: true,
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index f570c6d15672..7e4e4022f00f 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1374,7 +1374,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// The requested visibilities should be delayed as well. Otherwise, we might override
// the insets visibility before playing animation.
- setRequestedVisibleTypes(mReportedRequestedVisibleTypes, typesReady);
+ setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types);
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
if (!fromIme) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 41ef44e1ac1f..40b060ad0bbf 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -4361,15 +4361,14 @@ public final class InputMethodManager {
* @param icProto {@link InputConnection} call data in proto format.
* @hide
*/
- @GuardedBy("mH")
public void dumpDebug(ProtoOutputStream proto, @Nullable byte[] icProto) {
- if (!isImeSessionAvailableLocked()) {
- return;
- }
-
- proto.write(DISPLAY_ID, mDisplayId);
- final long token = proto.start(INPUT_METHOD_MANAGER);
synchronized (mH) {
+ if (!isImeSessionAvailableLocked()) {
+ return;
+ }
+
+ proto.write(DISPLAY_ID, mDisplayId);
+ final long token = proto.start(INPUT_METHOD_MANAGER);
proto.write(CUR_ID, mCurBindState.mImeId);
proto.write(FULLSCREEN_MODE, mFullscreenMode);
proto.write(ACTIVE, mActive);
diff --git a/core/java/android/view/inputmethod/TextAppearanceInfo.java b/core/java/android/view/inputmethod/TextAppearanceInfo.java
index 05717dd5d930..7eee33f7f617 100644
--- a/core/java/android/view/inputmethod/TextAppearanceInfo.java
+++ b/core/java/android/view/inputmethod/TextAppearanceInfo.java
@@ -238,7 +238,10 @@ public final class TextAppearanceInfo implements Parcelable {
.setFontFeatureSettings(textPaint.getFontFeatureSettings())
.setFontVariationSettings(textPaint.getFontVariationSettings())
.setTextScaleX(textPaint.getTextScaleX())
- .setTextColor(textPaint.getColor())
+ // When there is a hint text (text length is 0), the text color should be the normal
+ // text color rather than hint text color.
+ .setTextColor(text.length() == 0
+ ? textView.getCurrentTextColor() : textPaint.getColor())
.setLinkTextColor(textPaint.linkColor)
.setAllCaps(textView.isAllCaps())
.setFallbackLineSpacing(textView.isFallbackLineSpacing())
diff --git a/core/java/com/android/internal/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/common/ProtoLog.java
index 93765cdf0890..8870096f3db7 100644
--- a/core/java/com/android/internal/protolog/common/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/common/ProtoLog.java
@@ -16,6 +16,8 @@
package com.android.internal.protolog.common;
+import android.util.Log;
+
/**
* ProtoLog API - exposes static logging methods. Usage of this API is similar
* to {@code android.utils.Log} class. Instead of plain text log messages each call consists of
@@ -53,6 +55,9 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.d(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -68,6 +73,9 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.v(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -83,6 +91,9 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.i(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -98,6 +109,9 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.w(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -113,6 +127,9 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.e(group.getTag(), String.format(messageString, args));
+ }
}
/**
@@ -128,5 +145,8 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
+ if (group.isLogToLogcat()) {
+ Log.wtf(group.getTag(), String.format(messageString, args));
+ }
}
}
diff --git a/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java
index f93cd18a8521..0750cf1a64ab 100644
--- a/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java
@@ -37,6 +37,7 @@ import android.text.style.ForegroundColorSpan;
import android.text.style.ScaleXSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
+import android.text.util.Linkify;
import android.view.ViewGroup;
import android.widget.EditText;
@@ -53,7 +54,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class TextAppearanceInfoTest {
private static final float EPSILON = 0.0000001f;
- private static final String TEST_TEXT = "Happy birthday!";
+ private static final String TEST_TEXT = "Hello: google.com";
private static final float TEXT_SIZE = 16.5f;
private static final LocaleList TEXT_LOCALES = LocaleList.forLanguageTags("en,ja");
private static final String FONT_FAMILY_NAME = "sans-serif";
@@ -84,39 +85,7 @@ public class TextAppearanceInfoTest {
@Before
public void setUp() {
- mEditText.setText(mSpannableText);
- mEditText.getPaint().setTextSize(TEXT_SIZE);
- mEditText.setTextLocales(TEXT_LOCALES);
- Typeface family = Typeface.create(FONT_FAMILY_NAME, Typeface.NORMAL);
- mEditText.setTypeface(
- Typeface.create(family, TEXT_WEIGHT, (TEXT_STYLE & Typeface.ITALIC) != 0));
- mEditText.setAllCaps(ALL_CAPS);
- mEditText.setShadowLayer(SHADOW_RADIUS, SHADOW_DX, SHADOW_DY, SHADOW_COLOR);
- mEditText.setElegantTextHeight(ELEGANT_TEXT_HEIGHT);
- mEditText.setFallbackLineSpacing(FALLBACK_LINE_SPACING);
- mEditText.setLetterSpacing(LETTER_SPACING);
- mEditText.setFontFeatureSettings(FONT_FEATURE_SETTINGS);
- mEditText.setFontVariationSettings(FONT_VARIATION_SETTINGS);
- mEditText.setLineBreakStyle(LINE_BREAK_STYLE);
- mEditText.setLineBreakWordStyle(LINE_BREAK_WORD_STYLE);
- mEditText.setTextScaleX(TEXT_SCALEX);
- mEditText.setHighlightColor(HIGHLIGHT_TEXT_COLOR);
- mEditText.setTextColor(TEXT_COLOR);
- mEditText.setHintTextColor(HINT_TEXT_COLOR);
- mEditText.setLinkTextColor(LINK_TEXT_COLOR);
- ViewGroup.LayoutParams params =
- new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- mEditText.setLayoutParams(params);
- mEditText.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- Bitmap bitmap =
- Bitmap.createBitmap(
- Math.max(1, mEditText.getMeasuredWidth()),
- Math.max(1, mEditText.getMeasuredHeight()),
- Bitmap.Config.ARGB_8888);
- mEditText.layout(0, 0, mEditText.getMeasuredWidth(), mEditText.getMeasuredHeight());
- mCanvas = new Canvas(bitmap);
- mEditText.draw(mCanvas);
+ initEditText(mSpannableText);
}
@Test
@@ -233,6 +202,43 @@ public class TextAppearanceInfoTest {
assertEquals(info1.getSystemFontFamilyName(), FONT_FAMILY_NAME);
}
+ private void initEditText(CharSequence text) {
+ mEditText.setText(text);
+ mEditText.getPaint().setTextSize(TEXT_SIZE);
+ mEditText.setTextLocales(TEXT_LOCALES);
+ Typeface family = Typeface.create(FONT_FAMILY_NAME, Typeface.NORMAL);
+ mEditText.setTypeface(
+ Typeface.create(family, TEXT_WEIGHT, (TEXT_STYLE & Typeface.ITALIC) != 0));
+ mEditText.setAllCaps(ALL_CAPS);
+ mEditText.setShadowLayer(SHADOW_RADIUS, SHADOW_DX, SHADOW_DY, SHADOW_COLOR);
+ mEditText.setElegantTextHeight(ELEGANT_TEXT_HEIGHT);
+ mEditText.setFallbackLineSpacing(FALLBACK_LINE_SPACING);
+ mEditText.setLetterSpacing(LETTER_SPACING);
+ mEditText.setFontFeatureSettings(FONT_FEATURE_SETTINGS);
+ mEditText.setFontVariationSettings(FONT_VARIATION_SETTINGS);
+ mEditText.setLineBreakStyle(LINE_BREAK_STYLE);
+ mEditText.setLineBreakWordStyle(LINE_BREAK_WORD_STYLE);
+ mEditText.setTextScaleX(TEXT_SCALEX);
+ mEditText.setHighlightColor(HIGHLIGHT_TEXT_COLOR);
+ mEditText.setTextColor(TEXT_COLOR);
+ mEditText.setHintTextColor(HINT_TEXT_COLOR);
+ mEditText.setHint("Hint text");
+ mEditText.setLinkTextColor(LINK_TEXT_COLOR);
+ mEditText.setAutoLinkMask(Linkify.WEB_URLS);
+ ViewGroup.LayoutParams params =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ mEditText.setLayoutParams(params);
+ mEditText.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ Bitmap bitmap =
+ Bitmap.createBitmap(
+ Math.max(1, mEditText.getMeasuredWidth()),
+ Math.max(1, mEditText.getMeasuredHeight()),
+ Bitmap.Config.ARGB_8888);
+ mEditText.layout(0, 0, mEditText.getMeasuredWidth(), mEditText.getMeasuredHeight());
+ mCanvas = new Canvas(bitmap);
+ mEditText.draw(mCanvas);
+ }
private void assertTextAppearanceInfoContentsEqual(TextAppearanceInfo textAppearanceInfo) {
assertEquals(textAppearanceInfo.getTextSize(), TEXT_SIZE, EPSILON);
assertEquals(textAppearanceInfo.getTextLocales(), TEXT_LOCALES);
@@ -258,6 +264,15 @@ public class TextAppearanceInfoTest {
assertEquals(textAppearanceInfo.getLinkTextColor(), LINK_TEXT_COLOR);
}
+ @Test
+ public void testCreateFromTextView_withHintText() {
+ // Make hint text display
+ initEditText("");
+
+ // The text color should not be hint color
+ assertTextAppearanceInfoContentsEqual(TextAppearanceInfo.createFromTextView(mEditText));
+ }
+
static class CustomForegroundColorSpan extends ForegroundColorSpan {
@Nullable public TextPaint lastTextPaint = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index dc0a2943d394..f9d615ad0cf6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1079,10 +1079,28 @@ public class PipTransition extends PipTransitionController {
}
final float alphaStart = show ? 0 : 1;
final float alphaEnd = show ? 1 : 0;
+ final PipAnimationController.PipTransactionHandler transactionHandler =
+ new PipAnimationController.PipTransactionHandler() {
+ @Override
+ public boolean handlePipTransaction(SurfaceControl leash,
+ SurfaceControl.Transaction tx, Rect destinationBounds, float alpha) {
+ if (alpha == 0) {
+ if (show) {
+ tx.setPosition(leash, destinationBounds.left, destinationBounds.top);
+ } else {
+ // Put PiP out of the display so it won't block touch when it is hidden.
+ final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
+ final int max = Math.max(displayBounds.width(), displayBounds.height());
+ tx.setPosition(leash, max, max);
+ }
+ }
+ return false;
+ }
+ };
mPipAnimationController
.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), alphaStart, alphaEnd)
.setTransitionDirection(TRANSITION_DIRECTION_SAME)
- .setPipAnimationCallback(mPipAnimationCallback)
+ .setPipTransactionHandler(transactionHandler)
.setDuration(mEnterExitAnimationDuration)
.start();
mHasFadeOut = !show;
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
index ffe6d6a2e7af..991d7b5480c4 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
@@ -15,46 +15,47 @@
~ limitations under the License.
-->
<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
- <option name="test-tag" value="FlickerTests" />
+ <option name="test-tag" value="FlickerTests"/>
<!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
- <option name="isolated-storage" value="false" />
+ <option name="isolated-storage" value="false"/>
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
<!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on" />
+ <option name="screen-always-on" value="on"/>
<!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true" />
+ <option name="force-skip-system-props" value="true"/>
<!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all" />
+ <option name="run-command" value="cmd window tracing level all"/>
<!-- set WM tracing to frame (avoid incomplete states) -->
- <option name="run-command" value="cmd window tracing frame" />
+ <option name="run-command" value="cmd window tracing frame"/>
<!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
- <option name="run-command" value="pm disable com.google.android.internal.betterbug" />
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
<!-- ensure lock screen mode is swipe -->
- <option name="run-command" value="locksettings set-disabled false" />
+ <option name="run-command" value="locksettings set-disabled false"/>
<!-- restart launcher to activate TAPL -->
- <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
- <!-- Ensure output directory is empty at the start -->
- <option name="run-command" value="rm -rf /sdcard/flicker" />
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
<!-- Increase trace size: 20mb for WM and 80mb for SF -->
- <option name="run-command" value="cmd window tracing size 20480" />
- <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920" />
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
- <option name="run-command" value="settings put system show_touches 1" />
- <option name="run-command" value="settings put system pointer_location 1" />
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
<option name="teardown-command"
- value="settings delete secure show_ime_with_hard_keyboard" />
- <option name="teardown-command" value="settings delete system show_touches" />
- <option name="teardown-command" value="settings delete system pointer_location" />
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
<option name="teardown-command"
- value="cmd overlay enable com.android.internal.systemui.navbar.gestural" />
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="{MODULE}.apk"/>
- <option name="test-file-name" value="FlickerTestApp.apk" />
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
</target_preparer>
<!-- Needed for pushing the trace config file -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
@@ -65,35 +66,34 @@
/>
<!--Install the content provider automatically when we push some file in sdcard folder.-->
<!--Needed to avoid the installation during the test suite.-->
- <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto" />
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="{PACKAGE}"/>
- <option name="shell-timeout" value="6600s" />
- <option name="test-timeout" value="6000s" />
- <option name="hidden-api-checks" value="false" />
- <option name="device-listeners" value="android.device.collectors.PerfettoListener" />
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6000s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
<!-- PerfettoListener related arguments -->
- <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
<option name="instrumentation-arg"
key="perfetto_config_file"
value="trace_config.textproto"
/>
- <option name="instrumentation-arg" key="per_run" value="true" />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="pull-pattern-keys" value="perfetto_file_path" />
- </metrics_collector>
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="pull-pattern-keys" value="(\w)+\.winscope" />
- <option name="pull-pattern-keys" value="(\w)+\.mp4" />
- <option name="collect-on-run-ended-only" value="false" />
- <option name="clean-up" value="true" />
- </metrics_collector>
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/flicker" />
- <option name="collect-on-run-ended-only" value="true" />
- <option name="clean-up" value="true" />
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.pip/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.splitscreen/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
</metrics_collector>
</configuration>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
index 2bca7cf9762e..1ff2befd19ed 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
@@ -43,5 +43,5 @@ public final class BluetoothBroadcastUtils {
/**
* Bluetooth scheme.
*/
- public static final String SCHEME_BT_BROADCAST_METADATA = "BT:BluetoothLeBroadcastMetadata:";
+ public static final String SCHEME_BT_BROADCAST_METADATA = "BT:";
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
index b54b115213d5..9bb11f8da645 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
@@ -15,23 +15,92 @@
*/
package com.android.settingslib.bluetooth
+import android.annotation.TargetApi
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothLeAudioCodecConfigMetadata
+import android.bluetooth.BluetoothLeAudioContentMetadata
+import android.bluetooth.BluetoothLeBroadcastChannel
import android.bluetooth.BluetoothLeBroadcastMetadata
-import android.os.Parcel
-import android.os.Parcelable
+import android.bluetooth.BluetoothLeBroadcastSubgroup
+import android.os.Build
import android.util.Base64
import android.util.Log
import com.android.settingslib.bluetooth.BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA
object BluetoothLeBroadcastMetadataExt {
- private const val TAG = "BluetoothLeBroadcastMetadataExt"
+ private const val TAG = "BtLeBroadcastMetadataExt"
+
+ // BluetoothLeBroadcastMetadata
+ private const val KEY_BT_QR_VER = "R"
+ private const val KEY_BT_ADDRESS_TYPE = "T"
+ private const val KEY_BT_DEVICE = "D"
+ private const val KEY_BT_ADVERTISING_SID = "AS"
+ private const val KEY_BT_BROADCAST_ID = "B"
+ private const val KEY_BT_BROADCAST_NAME = "BN"
+ private const val KEY_BT_PUBLIC_BROADCAST_DATA = "PM"
+ private const val KEY_BT_SYNC_INTERVAL = "SI"
+ private const val KEY_BT_BROADCAST_CODE = "C"
+ private const val KEY_BT_SUBGROUPS = "SG"
+ private const val KEY_BT_VENDOR_SPECIFIC = "V"
+ private const val KEY_BT_ANDROID_VERSION = "VN"
+
+ // Subgroup data
+ private const val KEY_BTSG_BIS_SYNC = "BS"
+ private const val KEY_BTSG_BIS_MASK = "BM"
+ private const val KEY_BTSG_AUDIO_CONTENT = "AC"
+
+ // Vendor specific data
+ private const val KEY_BTVSD_COMPANY_ID = "VI"
+ private const val KEY_BTVSD_VENDOR_DATA = "VD"
+
+ private const val DELIMITER_KEY_VALUE = ":"
+ private const val DELIMITER_BT_LEVEL_1 = ";"
+ private const val DELIMITER_BT_LEVEL_2 = ","
+
+ private const val SUFFIX_QR_CODE = ";;"
+
+ private const val ANDROID_VER = "U"
+ private const val QR_CODE_VER = 0x010000
+
+ // BT constants
+ private const val BIS_SYNC_MAX_CHANNEL = 32
+ private const val BIS_SYNC_NO_PREFERENCE = 0xFFFFFFFFu
+ private const val SUBGROUP_LC3_CODEC_ID = 0x6L
/**
* Converts [BluetoothLeBroadcastMetadata] to QR code string.
*
- * QR code string will prefix with "BT:BluetoothLeBroadcastMetadata:".
+ * QR code string will prefix with "BT:".
*/
- fun BluetoothLeBroadcastMetadata.toQrCodeString(): String =
- SCHEME_BT_BROADCAST_METADATA + Base64.encodeToString(toBytes(this), Base64.NO_WRAP)
+ fun BluetoothLeBroadcastMetadata.toQrCodeString(): String {
+ val entries = mutableListOf<Pair<String, String>>()
+ entries.add(Pair(KEY_BT_QR_VER, QR_CODE_VER.toString()))
+ entries.add(Pair(KEY_BT_ADDRESS_TYPE, this.sourceAddressType.toString()))
+ entries.add(Pair(KEY_BT_DEVICE, this.sourceDevice.address.replace(":", "-")))
+ entries.add(Pair(KEY_BT_ADVERTISING_SID, this.sourceAdvertisingSid.toString()))
+ entries.add(Pair(KEY_BT_BROADCAST_ID, this.broadcastId.toString()))
+ if (this.broadcastName != null) {
+ entries.add(Pair(KEY_BT_BROADCAST_NAME, Base64.encodeToString(
+ this.broadcastName?.toByteArray(Charsets.UTF_8), Base64.NO_WRAP)))
+ }
+ if (this.publicBroadcastMetadata != null) {
+ entries.add(Pair(KEY_BT_PUBLIC_BROADCAST_DATA, Base64.encodeToString(
+ this.publicBroadcastMetadata?.rawMetadata, Base64.NO_WRAP)))
+ }
+ entries.add(Pair(KEY_BT_SYNC_INTERVAL, this.paSyncInterval.toString()))
+ if (this.broadcastCode != null) {
+ entries.add(Pair(KEY_BT_BROADCAST_CODE,
+ Base64.encodeToString(this.broadcastCode, Base64.NO_WRAP)))
+ }
+ this.subgroups.forEach {
+ subgroup -> entries.add(Pair(KEY_BT_SUBGROUPS, subgroup.toQrCodeString())) }
+ entries.add(Pair(KEY_BT_ANDROID_VERSION, ANDROID_VER))
+ val qrCodeString = SCHEME_BT_BROADCAST_METADATA +
+ entries.toQrCodeString(DELIMITER_BT_LEVEL_1) + SUFFIX_QR_CODE
+ Log.d(TAG, "Generated QR string : $qrCodeString")
+ return qrCodeString
+ }
/**
* Converts QR code string to [BluetoothLeBroadcastMetadata].
@@ -39,32 +108,255 @@ object BluetoothLeBroadcastMetadataExt {
* QR code string should prefix with "BT:BluetoothLeBroadcastMetadata:".
*/
fun convertToBroadcastMetadata(qrCodeString: String): BluetoothLeBroadcastMetadata? {
- if (!qrCodeString.startsWith(SCHEME_BT_BROADCAST_METADATA)) return null
+ if (!qrCodeString.startsWith(SCHEME_BT_BROADCAST_METADATA)) {
+ Log.e(TAG, "String \"$qrCodeString\" does not begin with " +
+ "\"$SCHEME_BT_BROADCAST_METADATA\"")
+ return null
+ }
return try {
- val encodedString = qrCodeString.removePrefix(SCHEME_BT_BROADCAST_METADATA)
- val bytes = Base64.decode(encodedString, Base64.NO_WRAP)
- createFromBytes(BluetoothLeBroadcastMetadata.CREATOR, bytes)
+ Log.d(TAG, "Parsing QR string: $qrCodeString")
+ val strippedString =
+ qrCodeString.removePrefix(SCHEME_BT_BROADCAST_METADATA)
+ .removeSuffix(SUFFIX_QR_CODE)
+ Log.d(TAG, "Stripped to: $strippedString")
+ parseQrCodeToMetadata(strippedString)
} catch (e: Exception) {
- Log.w(TAG, "Cannot convert QR code string to BluetoothLeBroadcastMetadata", e)
+ Log.w(TAG, "Cannot parse: $qrCodeString", e)
null
}
}
- private fun toBytes(parcelable: Parcelable): ByteArray =
- Parcel.obtain().run {
- parcelable.writeToParcel(this, 0)
- setDataPosition(0)
- val bytes = marshall()
- recycle()
- bytes
+ private fun BluetoothLeBroadcastSubgroup.toQrCodeString(): String {
+ val entries = mutableListOf<Pair<String, String>>()
+ entries.add(Pair(KEY_BTSG_BIS_SYNC, getBisSyncFromChannels(this.channels).toString()))
+ entries.add(Pair(KEY_BTSG_BIS_MASK, getBisMaskFromChannels(this.channels).toString()))
+ entries.add(Pair(KEY_BTSG_AUDIO_CONTENT,
+ Base64.encodeToString(this.contentMetadata.rawMetadata, Base64.NO_WRAP)))
+ return entries.toQrCodeString(DELIMITER_BT_LEVEL_2)
+ }
+
+ private fun List<Pair<String, String>>.toQrCodeString(delimiter: String): String {
+ val entryStrings = this.map{ it.first + DELIMITER_KEY_VALUE + it.second }
+ return entryStrings.joinToString(separator = delimiter)
+ }
+
+ @TargetApi(Build.VERSION_CODES.TIRAMISU)
+ private fun parseQrCodeToMetadata(input: String): BluetoothLeBroadcastMetadata {
+ // Split into a list of list
+ val level1Fields = input.split(DELIMITER_BT_LEVEL_1)
+ .map{it.split(DELIMITER_KEY_VALUE, limit = 2)}
+ var qrCodeVersion = -1
+ var sourceAddrType = BluetoothDevice.ADDRESS_TYPE_UNKNOWN
+ var sourceAddrString: String? = null
+ var sourceAdvertiserSid = -1
+ var broadcastId = -1
+ var broadcastName: String? = null
+ var publicBroadcastMetadata: BluetoothLeAudioContentMetadata? = null
+ var paSyncInterval = -1
+ var broadcastCode: ByteArray? = null
+ // List of VendorID -> Data Pairs
+ var vendorDataList = mutableListOf<Pair<Int, ByteArray?>>()
+ var androidVersion: String? = null
+ val builder = BluetoothLeBroadcastMetadata.Builder()
+
+ for (field: List<String> in level1Fields) {
+ if (field.isEmpty()) {
+ continue
+ }
+ val key = field[0]
+ // Ignore 3rd value and after
+ val value = if (field.size > 1) field[1] else ""
+ when (key) {
+ KEY_BT_QR_VER -> {
+ require(qrCodeVersion == -1) { "Duplicate qrCodeVersion: $input" }
+ qrCodeVersion = value.toInt()
+ }
+ KEY_BT_ADDRESS_TYPE -> {
+ require(sourceAddrType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) {
+ "Duplicate sourceAddrType: $input"
+ }
+ sourceAddrType = value.toInt()
+ }
+ KEY_BT_DEVICE -> {
+ require(sourceAddrString == null) { "Duplicate sourceAddr: $input" }
+ sourceAddrString = value.replace("-", ":")
+ }
+ KEY_BT_ADVERTISING_SID -> {
+ require(sourceAdvertiserSid == -1) { "Duplicate sourceAdvertiserSid: $input" }
+ sourceAdvertiserSid = value.toInt()
+ }
+ KEY_BT_BROADCAST_ID -> {
+ require(broadcastId == -1) { "Duplicate broadcastId: $input" }
+ broadcastId = value.toInt()
+ }
+ KEY_BT_BROADCAST_NAME -> {
+ require(broadcastName == null) { "Duplicate broadcastName: $input" }
+ broadcastName = String(Base64.decode(value, Base64.NO_WRAP))
+ }
+ KEY_BT_PUBLIC_BROADCAST_DATA -> {
+ require(publicBroadcastMetadata == null) {
+ "Duplicate publicBroadcastMetadata $input"
+ }
+ publicBroadcastMetadata = BluetoothLeAudioContentMetadata
+ .fromRawBytes(Base64.decode(value, Base64.NO_WRAP))
+ }
+ KEY_BT_SYNC_INTERVAL -> {
+ require(paSyncInterval == -1) { "Duplicate paSyncInterval: $input" }
+ paSyncInterval = value.toInt()
+ }
+ KEY_BT_BROADCAST_CODE -> {
+ require(broadcastCode == null) { "Duplicate broadcastCode: $input" }
+ broadcastCode = Base64.decode(value, Base64.NO_WRAP)
+ }
+ KEY_BT_ANDROID_VERSION -> {
+ require(androidVersion == null) { "Duplicate androidVersion: $input" }
+ androidVersion = value
+ Log.i(TAG, "QR code Android version: $androidVersion")
+ }
+ // Repeatable
+ KEY_BT_SUBGROUPS -> {
+ builder.addSubgroup(parseSubgroupData(value))
+ }
+ // Repeatable
+ KEY_BT_VENDOR_SPECIFIC -> {
+ vendorDataList.add(parseVendorData(value))
+ }
+ }
+ }
+ Log.d(TAG, "parseQrCodeToMetadata: sourceAddrType=$sourceAddrType, " +
+ "sourceAddr=$sourceAddrString, sourceAdvertiserSid=$sourceAdvertiserSid, " +
+ "broadcastId=$broadcastId, broadcastName=$broadcastName, " +
+ "publicBroadcastMetadata=${publicBroadcastMetadata != null}, " +
+ "paSyncInterval=$paSyncInterval, " +
+ "broadcastCode=${broadcastCode?.toString(Charsets.UTF_8)}")
+ Log.d(TAG, "Not used in current code, but part of the specification: " +
+ "qrCodeVersion=$qrCodeVersion, androidVersion=$androidVersion, " +
+ "vendorDataListSize=${vendorDataList.size}")
+ val adapter = BluetoothAdapter.getDefaultAdapter()
+ // add source device and set broadcast code
+ val device = adapter.getRemoteLeDevice(requireNotNull(sourceAddrString), sourceAddrType)
+ builder.apply {
+ setSourceDevice(device, sourceAddrType)
+ setSourceAdvertisingSid(sourceAdvertiserSid)
+ setBroadcastId(broadcastId)
+ setBroadcastName(broadcastName)
+ setPublicBroadcast(publicBroadcastMetadata != null)
+ setPublicBroadcastMetadata(publicBroadcastMetadata)
+ setPaSyncInterval(paSyncInterval)
+ setEncrypted(broadcastCode != null)
+ setBroadcastCode(broadcastCode)
+ // Presentation delay is unknown and not useful when adding source
+ // Broadcast sink needs to sync to the Broadcast source to get presentation delay
+ setPresentationDelayMicros(0)
+ }
+ return builder.build()
+ }
+
+ private fun parseSubgroupData(input: String): BluetoothLeBroadcastSubgroup {
+ Log.d(TAG, "parseSubgroupData: $input")
+ val fields = input.split(DELIMITER_BT_LEVEL_2)
+ var bisSync: UInt? = null
+ var bisMask: UInt? = null
+ var metadata: ByteArray? = null
+
+ fields.forEach { field ->
+ val(key, value) = field.split(DELIMITER_KEY_VALUE)
+ when (key) {
+ KEY_BTSG_BIS_SYNC -> {
+ require(bisSync == null) { "Duplicate bisSync: $input" }
+ bisSync = value.toUInt()
+ }
+ KEY_BTSG_BIS_MASK -> {
+ require(bisMask == null) { "Duplicate bisMask: $input" }
+ bisMask = value.toUInt()
+ }
+ KEY_BTSG_AUDIO_CONTENT -> {
+ require(metadata == null) { "Duplicate metadata: $input" }
+ metadata = Base64.decode(value, Base64.NO_WRAP)
+ }
+ }
}
+ val channels = convertToChannels(requireNotNull(bisSync), requireNotNull(bisMask))
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(0).build()
+ return BluetoothLeBroadcastSubgroup.Builder().apply {
+ setCodecId(SUBGROUP_LC3_CODEC_ID)
+ setCodecSpecificConfig(audioCodecConfigMetadata)
+ setContentMetadata(
+ BluetoothLeAudioContentMetadata.fromRawBytes(metadata ?: ByteArray(0)))
+ channels.forEach(::addChannel)
+ }.build()
+ }
+
+ private fun parseVendorData(input: String): Pair<Int, ByteArray?> {
+ var companyId = -1
+ var data: ByteArray? = null
+ val fields = input.split(DELIMITER_BT_LEVEL_2)
+ fields.forEach { field ->
+ val(key, value) = field.split(DELIMITER_KEY_VALUE)
+ when (key) {
+ KEY_BTVSD_COMPANY_ID -> {
+ require(companyId == -1) { "Duplicate companyId: $input" }
+ companyId = value.toInt()
+ }
+ KEY_BTVSD_VENDOR_DATA -> {
+ require(data == null) { "Duplicate data: $input" }
+ data = Base64.decode(value, Base64.NO_WRAP)
+ }
+ }
+ }
+ return Pair(companyId, data)
+ }
- private fun <T> createFromBytes(creator: Parcelable.Creator<T>, bytes: ByteArray): T =
- Parcel.obtain().run {
- unmarshall(bytes, 0, bytes.size)
- setDataPosition(0)
- val created = creator.createFromParcel(this)
- recycle()
- created
+ private fun getBisSyncFromChannels(channels: List<BluetoothLeBroadcastChannel>): UInt {
+ var bisSync = 0u
+ // channel index starts from 1
+ channels.forEach { channel ->
+ if (channel.isSelected && channel.channelIndex > 0) {
+ bisSync = bisSync or (1u shl (channel.channelIndex - 1))
+ }
}
+ // No channel is selected means no preference on Android platform
+ return if (bisSync == 0u) BIS_SYNC_NO_PREFERENCE else bisSync
+ }
+
+ private fun getBisMaskFromChannels(channels: List<BluetoothLeBroadcastChannel>): UInt {
+ var bisMask = 0u
+ // channel index starts from 1
+ channels.forEach { channel ->
+ if (channel.channelIndex > 0) {
+ bisMask = bisMask or (1u shl (channel.channelIndex - 1))
+ }
+ }
+ return bisMask
+ }
+
+ private fun convertToChannels(bisSync: UInt, bisMask: UInt):
+ List<BluetoothLeBroadcastChannel> {
+ Log.d(TAG, "convertToChannels: bisSync=$bisSync, bisMask=$bisMask")
+ var selectionMask = bisSync
+ if (bisSync != BIS_SYNC_NO_PREFERENCE) {
+ require(bisMask == (bisMask or bisSync)) {
+ "bisSync($bisSync) must select a subset of bisMask($bisMask) if it has preferences"
+ }
+ } else {
+ // No channel preference means no channel is selected
+ selectionMask = 0u
+ }
+ val channels = mutableListOf<BluetoothLeBroadcastChannel>()
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(0).build()
+ for (i in 0 until BIS_SYNC_MAX_CHANNEL) {
+ val channelMask = 1u shl i
+ if ((bisMask and channelMask) != 0u) {
+ val channel = BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected((selectionMask and channelMask) != 0u)
+ setChannelIndex(i + 1)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }
+ channels.add(channel.build())
+ }
+ }
+ return channels
+ }
} \ No newline at end of file
diff --git a/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt b/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
index 0e3590d96a14..27d7078774d5 100644
--- a/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
+++ b/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
@@ -34,24 +34,33 @@ class BluetoothLeBroadcastMetadataExtTest {
@Test
fun toQrCodeString() {
val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
- setCodecId(100)
+ setCodecId(0x6)
val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
setCodecSpecificConfig(audioCodecConfigMetadata)
- setContentMetadata(BluetoothLeAudioContentMetadata.Builder().build())
+ setContentMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("Test").setLanguage("eng").build())
addChannel(BluetoothLeBroadcastChannel.Builder().apply {
- setChannelIndex(1000)
+ setSelected(true)
+ setChannelIndex(2)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(true)
+ setChannelIndex(1)
setCodecMetadata(audioCodecConfigMetadata)
}.build())
}.build()
val metadata = BluetoothLeBroadcastMetadata.Builder().apply {
- setSourceDevice(Device, 0)
+ setSourceDevice(Device, BluetoothDevice.ADDRESS_TYPE_RANDOM)
setSourceAdvertisingSid(1)
- setBroadcastId(2)
- setPaSyncInterval(3)
+ setBroadcastId(123456)
+ setBroadcastName("Test")
+ setPublicBroadcastMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("pTest").build())
+ setPaSyncInterval(160)
setEncrypted(true)
- setBroadcastCode(byteArrayOf(10, 11, 12, 13))
- setPresentationDelayMicros(4)
+ setBroadcastCode("TestCode".toByteArray(Charsets.UTF_8))
addSubgroup(subgroup)
}.build()
@@ -61,6 +70,108 @@ class BluetoothLeBroadcastMetadataExtTest {
}
@Test
+ fun toQrCodeString_NoChannelSelected() {
+ val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
+ setCodecId(0x6)
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
+ setCodecSpecificConfig(audioCodecConfigMetadata)
+ setContentMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("Test").setLanguage("eng").build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(false)
+ setChannelIndex(2)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(false)
+ setChannelIndex(1)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ }.build()
+
+ val metadata = BluetoothLeBroadcastMetadata.Builder().apply {
+ setSourceDevice(Device, BluetoothDevice.ADDRESS_TYPE_RANDOM)
+ setSourceAdvertisingSid(1)
+ setBroadcastId(123456)
+ setBroadcastName("Test")
+ setPublicBroadcastMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("pTest").build())
+ setPaSyncInterval(160)
+ setEncrypted(true)
+ setBroadcastCode("TestCode".toByteArray(Charsets.UTF_8))
+ addSubgroup(subgroup)
+ }.build()
+
+ val qrCodeString = metadata.toQrCodeString()
+
+ val parsedMetadata =
+ BluetoothLeBroadcastMetadataExt.convertToBroadcastMetadata(qrCodeString)!!
+
+ assertThat(parsedMetadata).isNotNull()
+ assertThat(parsedMetadata.subgroups).isNotNull()
+ assertThat(parsedMetadata.subgroups.size).isEqualTo(1)
+ assertThat(parsedMetadata.subgroups[0].channels).isNotNull()
+ assertThat(parsedMetadata.subgroups[0].channels.size).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].hasChannelPreference()).isFalse()
+ // Input order does not matter due to parsing through bisMask
+ assertThat(parsedMetadata.subgroups[0].channels[0].channelIndex).isEqualTo(1)
+ assertThat(parsedMetadata.subgroups[0].channels[0].isSelected).isFalse()
+ assertThat(parsedMetadata.subgroups[0].channels[1].channelIndex).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].channels[1].isSelected).isFalse()
+ }
+
+ @Test
+ fun toQrCodeString_OneChannelSelected() {
+ val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
+ setCodecId(0x6)
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
+ setCodecSpecificConfig(audioCodecConfigMetadata)
+ setContentMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("Test").setLanguage("eng").build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(false)
+ setChannelIndex(1)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(true)
+ setChannelIndex(2)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ }.build()
+
+ val metadata = BluetoothLeBroadcastMetadata.Builder().apply {
+ setSourceDevice(Device, BluetoothDevice.ADDRESS_TYPE_RANDOM)
+ setSourceAdvertisingSid(1)
+ setBroadcastId(123456)
+ setBroadcastName("Test")
+ setPublicBroadcastMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo("pTest").build())
+ setPaSyncInterval(160)
+ setEncrypted(true)
+ setBroadcastCode("TestCode".toByteArray(Charsets.UTF_8))
+ addSubgroup(subgroup)
+ }.build()
+
+ val qrCodeString = metadata.toQrCodeString()
+
+ val parsedMetadata =
+ BluetoothLeBroadcastMetadataExt.convertToBroadcastMetadata(qrCodeString)!!
+
+ assertThat(parsedMetadata).isNotNull()
+ assertThat(parsedMetadata.subgroups).isNotNull()
+ assertThat(parsedMetadata.subgroups.size).isEqualTo(1)
+ assertThat(parsedMetadata.subgroups[0].channels).isNotNull()
+ // Only selected channel can be recovered
+ assertThat(parsedMetadata.subgroups[0].channels.size).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].hasChannelPreference()).isTrue()
+ assertThat(parsedMetadata.subgroups[0].channels[0].channelIndex).isEqualTo(1)
+ assertThat(parsedMetadata.subgroups[0].channels[0].isSelected).isFalse()
+ assertThat(parsedMetadata.subgroups[0].channels[1].channelIndex).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].channels[1].isSelected).isTrue()
+ }
+
+ @Test
fun decodeAndEncodeAgain_sameString() {
val metadata = BluetoothLeBroadcastMetadataExt.convertToBroadcastMetadata(QR_CODE_STRING)!!
@@ -73,13 +184,12 @@ class BluetoothLeBroadcastMetadataExtTest {
const val TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"
val Device: BluetoothDevice =
- BluetoothAdapter.getDefaultAdapter().getRemoteDevice(TEST_DEVICE_ADDRESS)
+ BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(TEST_DEVICE_ADDRESS,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM)
const val QR_CODE_STRING =
- "BT:BluetoothLeBroadcastMetadata:AAAAAAEAAAABAAAAEQAAADAAMAA6AEEAMQA6AEEAMQA6AEEAMQA6" +
- "AEEAMQA6AEEAMQAAAAAAAAABAAAAAgAAAAMAAAABAAAABAAAAAQAAAAKCwwNBAAAAAEAAAABAAAAZAAA" +
- "AAAAAAABAAAAAAAAAAAAAAAGAAAABgAAAAUDAAAAAAAAAAAAAAAAAAAAAAAAAQAAAP//////////AAAA" +
- "AAAAAAABAAAAAQAAAAAAAADoAwAAAQAAAAAAAAAAAAAABgAAAAYAAAAFAwAAAAAAAAAAAAAAAAAAAAAA" +
- "AAAAAAD/////AAAAAAAAAAA="
+ "BT:R:65536;T:1;D:00-A1-A1-A1-A1-A1;AS:1;B:123456;BN:VGVzdA==;" +
+ "PM:BgNwVGVzdA==;SI:160;C:VGVzdENvZGU=;SG:BS:3,BM:3,AC:BQNUZXN0BARlbmc=;" +
+ "VN:U;;"
}
} \ No newline at end of file
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
index 5d09de167dc5..4fe9f89830f6 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
@@ -59,5 +59,20 @@ object Easings {
/** The linear interpolator. */
val Linear = fromInterpolator(InterpolatorsAndroidX.LINEAR)
+ /** The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN. */
+ val Legacy = fromInterpolator(InterpolatorsAndroidX.LEGACY)
+
+ /**
+ * The default legacy accelerating interpolator as defined in Material 1. Also known as
+ * FAST_OUT_LINEAR_IN.
+ */
+ val LegacyAccelerate = fromInterpolator(InterpolatorsAndroidX.LEGACY_ACCELERATE)
+
+ /**
+ * T The default legacy decelerating interpolator as defined in Material 1. Also known as
+ * LINEAR_OUT_SLOW_IN.
+ */
+ val LegacyDecelerate = fromInterpolator(InterpolatorsAndroidX.LEGACY_DECELERATE)
+
private fun fromInterpolator(source: Interpolator) = Easing { x -> source.getInterpolation(x) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 20b859a6a242..f80143499928 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -19,23 +19,19 @@
package com.android.systemui.bouncer.ui.composable
import android.view.HapticFeedbackConstants
-import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.animation.core.Transition
+import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.keyframes
+import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.scaleIn
-import androidx.compose.animation.scaleOut
-import androidx.compose.animation.slideInHorizontally
-import androidx.compose.animation.slideOutHorizontally
-import androidx.compose.foundation.background
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectTapGestures
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -43,35 +39,40 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.systemui.R
+import com.android.systemui.bouncer.ui.viewmodel.EnteredKey
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.thenIf
-import kotlin.math.max
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit
import kotlinx.coroutines.async
@@ -86,8 +87,6 @@ internal fun PinBouncer(
// Report that the UI is shown to let the view-model run some logic.
LaunchedEffect(Unit) { viewModel.onShown() }
- // The length of the PIN input received so far, so we know how many dots to render.
- val pinLength: Pair<Int, Int> by viewModel.pinLengths.collectAsState()
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
@@ -103,30 +102,7 @@ internal fun PinBouncer(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
) {
- Row(
- horizontalArrangement = Arrangement.spacedBy(12.dp),
- modifier = Modifier.heightIn(min = 16.dp).animateContentSize(),
- ) {
- // TODO(b/281871687): add support for dot shapes.
- val (previousPinLength, currentPinLength) = pinLength
- val dotCount = max(previousPinLength, currentPinLength) + 1
- repeat(dotCount) { index ->
- AnimatedVisibility(
- visible = index < currentPinLength,
- enter = fadeIn() + scaleIn() + slideInHorizontally(),
- exit = fadeOut() + scaleOut() + slideOutHorizontally(),
- ) {
- Box(
- modifier =
- Modifier.size(16.dp)
- .background(
- MaterialTheme.colorScheme.onSurfaceVariant,
- CircleShape,
- )
- )
- }
- }
- }
+ PinInputDisplay(viewModel)
Spacer(Modifier.height(100.dp))
@@ -187,6 +163,148 @@ internal fun PinBouncer(
}
@Composable
+private fun PinInputDisplay(viewModel: PinBouncerViewModel) {
+ val currentPinEntries: List<EnteredKey> by viewModel.pinEntries.collectAsState()
+
+ // visiblePinEntries keeps pins removed from currentPinEntries in the composition until their
+ // disappear-animation completed. The list is sorted by the natural ordering of EnteredKey,
+ // which is guaranteed to produce the original edit order, since the model only modifies entries
+ // at the end.
+ val visiblePinEntries = remember { SnapshotStateList<EnteredKey>() }
+ currentPinEntries.forEach {
+ val index = visiblePinEntries.binarySearch(it)
+ if (index < 0) {
+ val insertionPoint = -(index + 1)
+ visiblePinEntries.add(insertionPoint, it)
+ }
+ }
+
+ Row(
+ modifier =
+ Modifier.heightIn(min = entryShapeSize)
+ // Pins overflowing horizontally should still be shown as scrolling.
+ .wrapContentSize(unbounded = true),
+ ) {
+ visiblePinEntries.forEachIndexed { index, entry ->
+ key(entry) {
+ val visibility = remember {
+ MutableTransitionState<EntryVisibility>(EntryVisibility.Hidden)
+ }
+ visibility.targetState =
+ when {
+ currentPinEntries.isEmpty() && visiblePinEntries.size > 1 ->
+ EntryVisibility.BulkHidden(index, visiblePinEntries.size)
+ currentPinEntries.contains(entry) -> EntryVisibility.Shown
+ else -> EntryVisibility.Hidden
+ }
+
+ ObscuredInputEntry(updateTransition(visibility, label = "Pin Entry $entry"))
+
+ LaunchedEffect(entry) {
+ // Remove entry from visiblePinEntries once the hide transition completed.
+ snapshotFlow {
+ visibility.currentState == visibility.targetState &&
+ visibility.targetState != EntryVisibility.Shown
+ }
+ .collect { isRemoved ->
+ if (isRemoved) {
+ visiblePinEntries.remove(entry)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+private sealed class EntryVisibility {
+ object Shown : EntryVisibility()
+
+ object Hidden : EntryVisibility()
+
+ /**
+ * Same as [Hidden], but applies when multiple entries are hidden simultaneously, without
+ * collapsing during the hide.
+ */
+ data class BulkHidden(val staggerIndex: Int, val totalEntryCount: Int) : EntryVisibility()
+}
+
+@Composable
+private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
+ // spec: http://shortn/_DEhE3Xl2bi
+ val shapePadding = 6.dp
+ val shapeOvershootSize = 22.dp
+ val dismissStaggerDelayMs = 33
+ val dismissDurationMs = 450
+ val expansionDurationMs = 250
+ val shapeExpandDurationMs = 83
+ val shapeRetractDurationMs = 167
+ val shapeCollapseDurationMs = 200
+
+ val animatedEntryWidth by
+ transition.animateDp(
+ transitionSpec = {
+ when (val target = targetState) {
+ is EntryVisibility.BulkHidden ->
+ // only collapse horizontal space once all entries are removed
+ snap(dismissDurationMs + dismissStaggerDelayMs * target.totalEntryCount)
+ else -> tween(expansionDurationMs, easing = Easings.Standard)
+ }
+ },
+ label = "entry space"
+ ) { state ->
+ if (state == EntryVisibility.Shown) entryShapeSize + (shapePadding * 2) else 0.dp
+ }
+
+ val animatedShapeSize by
+ transition.animateDp(
+ transitionSpec = {
+ when {
+ EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown ->
+ keyframes {
+ durationMillis = shapeExpandDurationMs + shapeRetractDurationMs
+ 0.dp at 0 with Easings.Linear
+ shapeOvershootSize at shapeExpandDurationMs with Easings.Legacy
+ }
+ targetState is EntryVisibility.BulkHidden -> {
+ val target = targetState as EntryVisibility.BulkHidden
+ tween(
+ dismissDurationMs,
+ delayMillis = target.staggerIndex * dismissStaggerDelayMs,
+ easing = Easings.Legacy,
+ )
+ }
+ else -> tween(shapeCollapseDurationMs, easing = Easings.StandardDecelerate)
+ }
+ },
+ label = "shape size"
+ ) { state ->
+ when (state) {
+ EntryVisibility.Shown -> entryShapeSize
+ else -> 0.dp
+ }
+ }
+
+ val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
+ Layout(
+ content = {
+ // TODO(b/282730134): add support for dot shapes.
+ Canvas(Modifier) { drawCircle(dotColor) }
+ }
+ ) { measurables, _ ->
+ val shapeSizePx = animatedShapeSize.roundToPx()
+ val placeable = measurables.single().measure(Constraints.fixed(shapeSizePx, shapeSizePx))
+
+ layout(animatedEntryWidth.roundToPx(), entryShapeSize.roundToPx()) {
+ placeable.place(
+ ((animatedEntryWidth - animatedShapeSize) / 2f).roundToPx(),
+ ((entryShapeSize - animatedShapeSize) / 2f).roundToPx()
+ )
+ }
+ }
+}
+
+@Composable
private fun PinDigit(
digit: Int,
contentColor: Color,
@@ -310,11 +428,13 @@ private fun showFailureAnimation() {
// TODO(b/282730134): implement.
}
+private val entryShapeSize = 16.dp
+
private val pinButtonSize = 84.dp
// Pin button motion spec: http://shortn/_9TTIG6SoEa
private val pinButtonPressedDuration = 100.milliseconds
-private val pinButtonPressedEasing = LinearEasing
+private val pinButtonPressedEasing = Easings.Linear
private val pinButtonHoldTime = 33.milliseconds
private val pinButtonReleasedDuration = 420.milliseconds
private val pinButtonReleasedEasing = Easings.Standard
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 7c76281e4bdd..0e20444347fd 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -45,6 +45,7 @@ import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
private val KEY_TIMESTAMP = "appliedTimestamp"
@@ -320,20 +321,20 @@ open class ClockRegistry(
}
}
- public fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) {
- scope.launch(bgDispatcher) { applySettings(mutator(settings ?: ClockSettings())) }
+ public suspend fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) {
+ withContext(bgDispatcher) { applySettings(mutator(settings ?: ClockSettings())) }
}
var currentClockId: ClockId
get() = settings?.clockId ?: fallbackClockId
set(value) {
- mutateSetting { it.copy(clockId = value) }
+ scope.launch(bgDispatcher) { mutateSetting { it.copy(clockId = value) } }
}
var seedColor: Int?
get() = settings?.seedColor
set(value) {
- mutateSetting { it.copy(seedColor = value) }
+ scope.launch(bgDispatcher) { mutateSetting { it.copy(seedColor = value) } }
}
init {
@@ -501,11 +502,25 @@ open class ClockRegistry(
fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId)
- fun registerClockChangeListener(listener: ClockChangeListener) =
+ /**
+ * Adds [listener] to receive future clock changes.
+ *
+ * Calling from main thread to make sure the access is thread safe.
+ */
+ fun registerClockChangeListener(listener: ClockChangeListener) {
+ assertMainThread()
clockChangeListeners.add(listener)
+ }
- fun unregisterClockChangeListener(listener: ClockChangeListener) =
+ /**
+ * Removes [listener] from future clock changes.
+ *
+ * Calling from main thread to make sure the access is thread safe.
+ */
+ fun unregisterClockChangeListener(listener: ClockChangeListener) {
+ assertMainThread()
clockChangeListeners.remove(listener)
+ }
fun createCurrentClock(): ClockController {
val clockId = currentClockId
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
index 6f363a4ffa26..08ee60204178 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
@@ -20,8 +20,6 @@ package com.android.systemui.shared.quickaffordance.shared.model
object KeyguardPreviewConstants {
const val MESSAGE_ID_HIDE_SMART_SPACE = 1111
const val KEY_HIDE_SMART_SPACE = "hide_smart_space"
- const val MESSAGE_ID_COLOR_OVERRIDE = 1234
- const val KEY_COLOR_OVERRIDE = "color_override" // ColorInt Encoded as string
const val MESSAGE_ID_SLOT_SELECTED = 1337
const val KEY_SLOT_ID = "slot_id"
const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
index 21218a2dc8e2..9f075e521126 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
@@ -16,13 +16,15 @@
package com.android.systemui.monet.dynamiccolor;
+import android.annotation.NonNull;
+
import com.android.systemui.monet.dislike.DislikeAnalyzer;
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.hct.ViewingConditions;
import com.android.systemui.monet.scheme.DynamicScheme;
import com.android.systemui.monet.scheme.Variant;
-/** Named colors, otherwise known as tokens, or roles, in the Material Design system.*/
+/** Named colors, otherwise known as tokens, or roles, in the Material Design system. */
// Prevent lint for Function.apply not being available on Android before API level 14 (4.0.1).
// "AndroidJdkLibsChecker" for Function, "NewApi" for Function.apply().
// A java_library Bazel rule with an Android constraint cannot skip these warnings without this
@@ -32,190 +34,148 @@ import com.android.systemui.monet.scheme.Variant;
public final class MaterialDynamicColors {
private static final double CONTAINER_ACCENT_TONE_DELTA = 15.0;
-
- public MaterialDynamicColors() {
- }
-
- /**
- * These colors were present in Android framework before Android U, and used by MDC controls.
- * They
- * should be avoided, if possible. It's unclear if they're used on multiple backgrounds, and if
- * they are, they can't be adjusted for contrast.* For now, they will be set with no background,
- * and those won't adjust for contrast, avoiding issues.
- *
- * <p>* For example, if the same color is on a white background _and_ black background,
- * there's no
- * way to increase contrast with either without losing contrast with the other.
- */
- // colorControlActivated documented as colorAccent in M3 & GM3.
- // colorAccent documented as colorSecondary in M3 and colorPrimary in GM3.
- // Android used Material's Container as Primary/Secondary/Tertiary at launch.
- // Therefore, this is a duplicated version of Primary Container.
- public static DynamicColor controlActivated() {
- return DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0, null);
- }
+ public MaterialDynamicColors() {}
// Compatibility Keys Colors for Android
- public static DynamicColor primaryPaletteKeyColor() {
+ public DynamicColor primaryPaletteKeyColor() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette, (s) -> s.primaryPalette.getKeyColor().getTone());
}
- public static DynamicColor secondaryPaletteKeyColor() {
+ public DynamicColor secondaryPaletteKeyColor() {
return DynamicColor.fromPalette(
(s) -> s.secondaryPalette, (s) -> s.secondaryPalette.getKeyColor().getTone());
}
- public static DynamicColor tertiaryPaletteKeyColor() {
+ public DynamicColor tertiaryPaletteKeyColor() {
return DynamicColor.fromPalette(
(s) -> s.tertiaryPalette, (s) -> s.tertiaryPalette.getKeyColor().getTone());
}
- public static DynamicColor neutralPaletteKeyColor() {
+ public DynamicColor neutralPaletteKeyColor() {
return DynamicColor.fromPalette(
(s) -> s.neutralPalette, (s) -> s.neutralPalette.getKeyColor().getTone());
}
- public static DynamicColor neutralVariantPaletteKeyColor() {
+ public DynamicColor neutralVariantPaletteKeyColor() {
return DynamicColor.fromPalette(
(s) -> s.neutralVariantPalette,
(s) -> s.neutralVariantPalette.getKeyColor().getTone());
}
- private static ViewingConditions viewingConditionsForAlbers(DynamicScheme scheme) {
- return ViewingConditions.defaultWithBackgroundLstar(scheme.isDark ? 30.0 : 80.0);
- }
-
- private static boolean isFidelity(DynamicScheme scheme) {
- return scheme.variant == Variant.FIDELITY || scheme.variant == Variant.CONTENT;
- }
-
- private static boolean isMonochrome(DynamicScheme scheme) {
- return scheme.variant == Variant.MONOCHROME;
- }
-
- static double findDesiredChromaByTone(
- double hue, double chroma, double tone, boolean byDecreasingTone) {
- double answer = tone;
-
- Hct closestToChroma = Hct.from(hue, chroma, tone);
- if (closestToChroma.getChroma() < chroma) {
- double chromaPeak = closestToChroma.getChroma();
- while (closestToChroma.getChroma() < chroma) {
- answer += byDecreasingTone ? -1.0 : 1.0;
- Hct potentialSolution = Hct.from(hue, chroma, answer);
- if (chromaPeak > potentialSolution.getChroma()) {
- break;
- }
- if (Math.abs(potentialSolution.getChroma() - chroma) < 0.4) {
- break;
- }
-
- double potentialDelta = Math.abs(potentialSolution.getChroma() - chroma);
- double currentDelta = Math.abs(closestToChroma.getChroma() - chroma);
- if (potentialDelta < currentDelta) {
- closestToChroma = potentialSolution;
- }
- chromaPeak = Math.max(chromaPeak, potentialSolution.getChroma());
- }
- }
-
- return answer;
- }
-
- static double performAlbers(Hct prealbers, DynamicScheme scheme) {
- final Hct albersd = prealbers.inViewingConditions(viewingConditionsForAlbers(scheme));
- if (DynamicColor.tonePrefersLightForeground(prealbers.getTone())
- && !DynamicColor.toneAllowsLightForeground(albersd.getTone())) {
- return DynamicColor.enableLightForeground(prealbers.getTone());
- } else {
- return DynamicColor.enableLightForeground(albersd.getTone());
- }
- }
-
- public static DynamicColor highestSurface(DynamicScheme s) {
+ @NonNull
+ public DynamicColor highestSurface(@NonNull DynamicScheme s) {
return s.isDark ? surfaceBright() : surfaceDim();
}
- public static DynamicColor background() {
+ @NonNull
+ public DynamicColor background() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
}
- public static DynamicColor onBackground() {
+ @NonNull
+ public DynamicColor onBackground() {
return DynamicColor.fromPalette(
(s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> background());
}
- public static DynamicColor surface() {
+ @NonNull
+ public DynamicColor surface() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
}
- public static DynamicColor inverseSurface() {
+ @NonNull
+ public DynamicColor inverseSurface() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 20.0);
}
- public static DynamicColor surfaceBright() {
+ @NonNull
+ public DynamicColor surfaceBright() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 24.0 : 98.0);
}
- public static DynamicColor surfaceDim() {
+ @NonNull
+ public DynamicColor surfaceDim() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 87.0);
}
- public static DynamicColor surfaceContainerLowest() {
+ @NonNull
+ public DynamicColor surfaceContainerLowest() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 4.0 : 100.0);
}
- public static DynamicColor surfaceContainerLow() {
+ @NonNull
+ public DynamicColor surfaceContainerLow() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 96.0);
}
- public static DynamicColor surfaceContainer() {
+ @NonNull
+ public DynamicColor surfaceContainer() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 12.0 : 94.0);
}
- public static DynamicColor surfaceContainerHigh() {
+ @NonNull
+ public DynamicColor surfaceContainerHigh() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 17.0 : 92.0);
}
- public static DynamicColor surfaceContainerHighest() {
+ @NonNull
+ public DynamicColor surfaceContainerHighest() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 22.0 : 90.0);
}
- public static DynamicColor onSurface() {
+ @NonNull
+ public DynamicColor onSurface() {
return DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0,
- MaterialDynamicColors::highestSurface);
+ (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, this::highestSurface);
}
- public static DynamicColor inverseOnSurface() {
+ @NonNull
+ public DynamicColor inverseOnSurface() {
return DynamicColor.fromPalette(
(s) -> s.neutralPalette, (s) -> s.isDark ? 20.0 : 95.0, (s) -> inverseSurface());
}
- public static DynamicColor surfaceVariant() {
- return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
- (s) -> s.isDark ? 30.0 : 90.0);
+ @NonNull
+ public DynamicColor surfaceVariant() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 90.0);
}
- public static DynamicColor onSurfaceVariant() {
+ @NonNull
+ public DynamicColor onSurfaceVariant() {
return DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0,
- (s) -> surfaceVariant());
+ (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0, (s) -> surfaceVariant());
}
- public static DynamicColor outline() {
+ @NonNull
+ public DynamicColor outline() {
return DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> 50.0, MaterialDynamicColors::highestSurface);
+ (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 60.0 : 50.0, this::highestSurface);
}
- public static DynamicColor outlineVariant() {
+ @NonNull
+ public DynamicColor outlineVariant() {
return DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0,
- MaterialDynamicColors::highestSurface);
+ (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0, this::highestSurface);
+ }
+
+ @NonNull
+ public DynamicColor shadow() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> 0.0);
}
- public static DynamicColor primaryContainer() {
+ @NonNull
+ public DynamicColor scrim() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> 0.0);
+ }
+
+ @NonNull
+ public DynamicColor surfaceTint() {
+ return DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 80.0 : 40.0);
+ }
+
+ @NonNull
+ public DynamicColor primaryContainer() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette,
(s) -> {
@@ -227,10 +187,11 @@ public final class MaterialDynamicColors {
}
return s.isDark ? 30.0 : 90.0;
},
- MaterialDynamicColors::highestSurface);
+ this::highestSurface);
}
- public static DynamicColor onPrimaryContainer() {
+ @NonNull
+ public DynamicColor onPrimaryContainer() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette,
(s) -> {
@@ -246,7 +207,8 @@ public final class MaterialDynamicColors {
null);
}
- public static DynamicColor primary() {
+ @NonNull
+ public DynamicColor primary() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette,
(s) -> {
@@ -255,7 +217,7 @@ public final class MaterialDynamicColors {
}
return s.isDark ? 80.0 : 40.0;
},
- MaterialDynamicColors::highestSurface,
+ this::highestSurface,
(s) ->
new ToneDeltaConstraint(
CONTAINER_ACCENT_TONE_DELTA,
@@ -263,12 +225,14 @@ public final class MaterialDynamicColors {
s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
}
- public static DynamicColor inversePrimary() {
+ @NonNull
+ public DynamicColor inversePrimary() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette, (s) -> s.isDark ? 40.0 : 80.0, (s) -> inverseSurface());
}
- public static DynamicColor onPrimary() {
+ @NonNull
+ public DynamicColor onPrimary() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette,
(s) -> {
@@ -280,7 +244,8 @@ public final class MaterialDynamicColors {
(s) -> primary());
}
- public static DynamicColor secondaryContainer() {
+ @NonNull
+ public DynamicColor secondaryContainer() {
return DynamicColor.fromPalette(
(s) -> s.secondaryPalette,
(s) -> {
@@ -300,10 +265,11 @@ public final class MaterialDynamicColors {
answer = performAlbers(s.secondaryPalette.getHct(answer), s);
return answer;
},
- MaterialDynamicColors::highestSurface);
+ this::highestSurface);
}
- public static DynamicColor onSecondaryContainer() {
+ @NonNull
+ public DynamicColor onSecondaryContainer() {
return DynamicColor.fromPalette(
(s) -> s.secondaryPalette,
(s) -> {
@@ -315,11 +281,12 @@ public final class MaterialDynamicColors {
(s) -> secondaryContainer());
}
- public static DynamicColor secondary() {
+ @NonNull
+ public DynamicColor secondary() {
return DynamicColor.fromPalette(
(s) -> s.secondaryPalette,
(s) -> s.isDark ? 80.0 : 40.0,
- MaterialDynamicColors::highestSurface,
+ this::highestSurface,
(s) ->
new ToneDeltaConstraint(
CONTAINER_ACCENT_TONE_DELTA,
@@ -327,7 +294,8 @@ public final class MaterialDynamicColors {
s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
}
- public static DynamicColor onSecondary() {
+ @NonNull
+ public DynamicColor onSecondary() {
return DynamicColor.fromPalette(
(s) -> s.secondaryPalette,
(s) -> {
@@ -339,7 +307,8 @@ public final class MaterialDynamicColors {
(s) -> secondary());
}
- public static DynamicColor tertiaryContainer() {
+ @NonNull
+ public DynamicColor tertiaryContainer() {
return DynamicColor.fromPalette(
(s) -> s.tertiaryPalette,
(s) -> {
@@ -354,10 +323,11 @@ public final class MaterialDynamicColors {
final Hct proposedHct = s.tertiaryPalette.getHct(albersTone);
return DislikeAnalyzer.fixIfDisliked(proposedHct).getTone();
},
- MaterialDynamicColors::highestSurface);
+ this::highestSurface);
}
- public static DynamicColor onTertiaryContainer() {
+ @NonNull
+ public DynamicColor onTertiaryContainer() {
return DynamicColor.fromPalette(
(s) -> s.tertiaryPalette,
(s) -> {
@@ -372,7 +342,8 @@ public final class MaterialDynamicColors {
(s) -> tertiaryContainer());
}
- public static DynamicColor tertiary() {
+ @NonNull
+ public DynamicColor tertiary() {
return DynamicColor.fromPalette(
(s) -> s.tertiaryPalette,
(s) -> {
@@ -381,7 +352,7 @@ public final class MaterialDynamicColors {
}
return s.isDark ? 80.0 : 40.0;
},
- MaterialDynamicColors::highestSurface,
+ this::highestSurface,
(s) ->
new ToneDeltaConstraint(
CONTAINER_ACCENT_TONE_DELTA,
@@ -389,7 +360,8 @@ public final class MaterialDynamicColors {
s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
}
- public static DynamicColor onTertiary() {
+ @NonNull
+ public DynamicColor onTertiary() {
return DynamicColor.fromPalette(
(s) -> s.tertiaryPalette,
(s) -> {
@@ -401,22 +373,24 @@ public final class MaterialDynamicColors {
(s) -> tertiary());
}
- public static DynamicColor errorContainer() {
+ @NonNull
+ public DynamicColor errorContainer() {
return DynamicColor.fromPalette(
- (s) -> s.errorPalette, (s) -> s.isDark ? 30.0 : 90.0,
- MaterialDynamicColors::highestSurface);
+ (s) -> s.errorPalette, (s) -> s.isDark ? 30.0 : 90.0, this::highestSurface);
}
- public static DynamicColor onErrorContainer() {
+ @NonNull
+ public DynamicColor onErrorContainer() {
return DynamicColor.fromPalette(
(s) -> s.errorPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> errorContainer());
}
- public static DynamicColor error() {
+ @NonNull
+ public DynamicColor error() {
return DynamicColor.fromPalette(
(s) -> s.errorPalette,
(s) -> s.isDark ? 80.0 : 40.0,
- MaterialDynamicColors::highestSurface,
+ this::highestSurface,
(s) ->
new ToneDeltaConstraint(
CONTAINER_ACCENT_TONE_DELTA,
@@ -424,113 +398,138 @@ public final class MaterialDynamicColors {
s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
}
- public static DynamicColor onError() {
+ @NonNull
+ public DynamicColor onError() {
return DynamicColor.fromPalette(
(s) -> s.errorPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> error());
}
- public static DynamicColor primaryFixed() {
+ @NonNull
+ public DynamicColor primaryFixed() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette,
(s) -> {
if (isMonochrome(s)) {
- return s.isDark ? 100.0 : 10.0;
+ return 40.0;
}
return 90.0;
},
- MaterialDynamicColors::highestSurface);
+ this::highestSurface);
}
- public static DynamicColor primaryFixedDim() {
+ @NonNull
+ public DynamicColor primaryFixedDim() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette,
(s) -> {
if (isMonochrome(s)) {
- return s.isDark ? 90.0 : 20.0;
+ return 30.0;
}
return 80.0;
},
- MaterialDynamicColors::highestSurface);
+ this::highestSurface);
}
- public static DynamicColor onPrimaryFixed() {
+ @NonNull
+ public DynamicColor onPrimaryFixed() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette,
(s) -> {
if (isMonochrome(s)) {
- return s.isDark ? 10.0 : 90.0;
+ return 100.0;
}
return 10.0;
},
(s) -> primaryFixedDim());
}
- public static DynamicColor onPrimaryFixedVariant() {
+ @NonNull
+ public DynamicColor onPrimaryFixedVariant() {
return DynamicColor.fromPalette(
(s) -> s.primaryPalette,
(s) -> {
if (isMonochrome(s)) {
- return s.isDark ? 30.0 : 70.0;
+ return 90.0;
}
return 30.0;
},
(s) -> primaryFixedDim());
}
- public static DynamicColor secondaryFixed() {
+ @NonNull
+ public DynamicColor secondaryFixed() {
return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 80.0 : 90.0,
- MaterialDynamicColors::highestSurface);
+ (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 80.0 : 90.0, this::highestSurface);
}
- public static DynamicColor secondaryFixedDim() {
+ @NonNull
+ public DynamicColor secondaryFixedDim() {
return DynamicColor.fromPalette(
- (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 70.0 : 80.0,
- MaterialDynamicColors::highestSurface);
+ (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 70.0 : 80.0, this::highestSurface);
}
- public static DynamicColor onSecondaryFixed() {
+ @NonNull
+ public DynamicColor onSecondaryFixed() {
return DynamicColor.fromPalette(
(s) -> s.secondaryPalette, (s) -> 10.0, (s) -> secondaryFixedDim());
}
- public static DynamicColor onSecondaryFixedVariant() {
+ @NonNull
+ public DynamicColor onSecondaryFixedVariant() {
return DynamicColor.fromPalette(
(s) -> s.secondaryPalette,
(s) -> isMonochrome(s) ? 25.0 : 30.0,
(s) -> secondaryFixedDim());
}
- public static DynamicColor tertiaryFixed() {
+ @NonNull
+ public DynamicColor tertiaryFixed() {
return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 40.0 : 90.0,
- MaterialDynamicColors::highestSurface);
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 40.0 : 90.0, this::highestSurface);
}
- public static DynamicColor tertiaryFixedDim() {
+ @NonNull
+ public DynamicColor tertiaryFixedDim() {
return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 30.0 : 80.0,
- MaterialDynamicColors::highestSurface);
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 30.0 : 80.0, this::highestSurface);
}
- public static DynamicColor onTertiaryFixed() {
+ @NonNull
+ public DynamicColor onTertiaryFixed() {
return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 90.0 : 10.0,
- (s) -> tertiaryFixedDim());
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 100.0 : 10.0, (s) -> tertiaryFixedDim());
}
- public static DynamicColor onTertiaryFixedVariant() {
+ @NonNull
+ public DynamicColor onTertiaryFixedVariant() {
return DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 70.0 : 30.0,
- (s) -> tertiaryFixedDim());
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 90.0 : 30.0, (s) -> tertiaryFixedDim());
+ }
+
+ /**
+ * These colors were present in Android framework before Android U, and used by MDC controls. They
+ * should be avoided, if possible. It's unclear if they're used on multiple backgrounds, and if
+ * they are, they can't be adjusted for contrast.* For now, they will be set with no background,
+ * and those won't adjust for contrast, avoiding issues.
+ *
+ * <p>* For example, if the same color is on a white background _and_ black background, there's no
+ * way to increase contrast with either without losing contrast with the other.
+ */
+ // colorControlActivated documented as colorAccent in M3 & GM3.
+ // colorAccent documented as colorSecondary in M3 and colorPrimary in GM3.
+ // Android used Material's Container as Primary/Secondary/Tertiary at launch.
+ // Therefore, this is a duplicated version of Primary Container.
+ @NonNull
+ public DynamicColor controlActivated() {
+ return DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0, null);
}
// colorControlNormal documented as textColorSecondary in M3 & GM3.
// In Material, textColorSecondary points to onSurfaceVariant in the non-disabled state,
// which is Neutral Variant T30/80 in light/dark.
- public static DynamicColor controlNormal() {
- return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
- (s) -> s.isDark ? 80.0 : 30.0);
+ @NonNull
+ public DynamicColor controlNormal() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0);
}
// colorControlHighlight documented, in both M3 & GM3:
@@ -541,7 +540,8 @@ public final class MaterialDynamicColors {
// DynamicColors do not support alpha currently, and _may_ not need it for this use case,
// depending on how MDC resolved alpha for the other cases.
// Returning black in dark mode, white in light mode.
- public static DynamicColor controlHighlight() {
+ @NonNull
+ public DynamicColor controlHighlight() {
return new DynamicColor(
s -> 0.0,
s -> 0.0,
@@ -549,40 +549,92 @@ public final class MaterialDynamicColors {
s -> s.isDark ? 0.20 : 0.12,
null,
scheme ->
-
- DynamicColor.toneMinContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null,
- scheme, null),
+ DynamicColor.toneMinContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
scheme ->
- DynamicColor.toneMaxContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null,
- scheme, null),
+ DynamicColor.toneMaxContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
null);
}
// textColorPrimaryInverse documented, in both M3 & GM3, documented as N10/N90.
- public static DynamicColor textPrimaryInverse() {
+ @NonNull
+ public DynamicColor textPrimaryInverse() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
}
// textColorSecondaryInverse and textColorTertiaryInverse both documented, in both M3 & GM3, as
// NV30/NV80
- public static DynamicColor textSecondaryAndTertiaryInverse() {
- return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
- (s) -> s.isDark ? 30.0 : 80.0);
+ @NonNull
+ public DynamicColor textSecondaryAndTertiaryInverse() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0);
}
// textColorPrimaryInverseDisableOnly documented, in both M3 & GM3, as N10/N90
- public static DynamicColor textPrimaryInverseDisableOnly() {
+ @NonNull
+ public DynamicColor textPrimaryInverseDisableOnly() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
}
// textColorSecondaryInverse and textColorTertiaryInverse in disabled state both documented,
// in both M3 & GM3, as N10/N90
- public static DynamicColor textSecondaryAndTertiaryInverseDisabled() {
+ @NonNull
+ public DynamicColor textSecondaryAndTertiaryInverseDisabled() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
}
// textColorHintInverse documented, in both M3 & GM3, as N10/N90
- public static DynamicColor textHintInverse() {
+ @NonNull
+ public DynamicColor textHintInverse() {
return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
}
-}
+
+ private static ViewingConditions viewingConditionsForAlbers(DynamicScheme scheme) {
+ return ViewingConditions.defaultWithBackgroundLstar(scheme.isDark ? 30.0 : 80.0);
+ }
+
+ private static boolean isFidelity(DynamicScheme scheme) {
+ return scheme.variant == Variant.FIDELITY || scheme.variant == Variant.CONTENT;
+ }
+
+ private static boolean isMonochrome(DynamicScheme scheme) {
+ return scheme.variant == Variant.MONOCHROME;
+ }
+
+ static double findDesiredChromaByTone(
+ double hue, double chroma, double tone, boolean byDecreasingTone) {
+ double answer = tone;
+
+ Hct closestToChroma = Hct.from(hue, chroma, tone);
+ if (closestToChroma.getChroma() < chroma) {
+ double chromaPeak = closestToChroma.getChroma();
+ while (closestToChroma.getChroma() < chroma) {
+ answer += byDecreasingTone ? -1.0 : 1.0;
+ Hct potentialSolution = Hct.from(hue, chroma, answer);
+ if (chromaPeak > potentialSolution.getChroma()) {
+ break;
+ }
+ if (Math.abs(potentialSolution.getChroma() - chroma) < 0.4) {
+ break;
+ }
+
+ double potentialDelta = Math.abs(potentialSolution.getChroma() - chroma);
+ double currentDelta = Math.abs(closestToChroma.getChroma() - chroma);
+ if (potentialDelta < currentDelta) {
+ closestToChroma = potentialSolution;
+ }
+ chromaPeak = Math.max(chromaPeak, potentialSolution.getChroma());
+ }
+ }
+
+ return answer;
+ }
+
+ static double performAlbers(Hct prealbers, DynamicScheme scheme) {
+ final Hct albersd = prealbers.inViewingConditions(viewingConditionsForAlbers(scheme));
+ if (DynamicColor.tonePrefersLightForeground(prealbers.getTone())
+ && !DynamicColor.toneAllowsLightForeground(albersd.getTone())) {
+ return DynamicColor.enableLightForeground(prealbers.getTone());
+ } else {
+ return DynamicColor.enableLightForeground(albersd.getTone());
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 02d55104c288..1f47e724e48c 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -64,15 +64,16 @@
# The plugins, log & common subpackages act as shared libraries that might be referenced in
# dynamically-loaded plugin APKs.
--keep class com.android.systemui.plugins.** {
- *;
-}
--keep class com.android.systemui.log.** {
- *;
-}
--keep class com.android.systemui.common.** {
- *;
-}
+-keep class com.android.systemui.plugins.** { *; }
+-keep class com.android.systemui.log.ConstantStringsLoggerImpl { *; }
+-keep class com.android.systemui.log.ConstantStringsLogger { *; }
+-keep class com.android.systemui.log.LogBuffer { *; }
+-keep class com.android.systemui.log.LogcatEchoTrackerDebug { *; }
+-keep class com.android.systemui.log.LogcatEchoTracker { *; }
+-keep class com.android.systemui.log.LogcatEchoTrackerProd { *; }
+-keep class com.android.systemui.log.LogLevel { *; }
+-keep class com.android.systemui.log.LogMessageImpl { *; }
+-keep class com.android.systemui.log.LogMessage { *; }
-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
*;
}
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index a3a7135dabad..66c57fc2a9ac 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -23,7 +23,7 @@
android:outlineProvider="none" >
<LinearLayout
- android:id="@+id/keyguard_indication_area"
+ android:id="@id/keyguard_indication_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
@@ -31,7 +31,7 @@
android:orientation="vertical">
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_text"
+ android:id="@id/keyguard_indication_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
@@ -41,13 +41,12 @@
android:accessibilityLiveRegion="polite"/>
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_text_bottom"
+ android:id="@id/keyguard_indication_text_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
- android:minHeight="48dp"
+ android:minHeight="@dimen/keyguard_indication_text_min_height"
android:layout_gravity="center_horizontal"
- android:layout_centerHorizontal="true"
android:paddingStart="@dimen/keyguard_indication_text_padding"
android:paddingEnd="@dimen/keyguard_indication_text_padding"
android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 01465d759ab0..6601e63fa034 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -70,6 +70,12 @@
android:layout_height="match_parent"
android:visibility="invisible" />
+ <!-- Root for all keyguard content. It was previously located within the shade. -->
+ <com.android.systemui.keyguard.ui.view.KeyguardRootView
+ android:id="@id/keyguard_root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<include layout="@layout/brightness_mirror_container" />
<com.android.systemui.scrim.ScrimView
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8d3ba364da06..50d33c6a733b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -275,6 +275,9 @@
<!-- The padding at start and end of indication text shown on AOD -->
<dimen name="keyguard_indication_text_padding">16dp</dimen>
+ <!-- The min height on the indication text shown on AOD -->
+ <dimen name="keyguard_indication_text_min_height">48dp</dimen>
+
<!-- Shadows under the clock, date and other keyguard text fields -->
<dimen name="keyguard_shadow_radius">5</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e5c946156e46..d651a2159721 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -206,5 +206,9 @@
<!-- keyboard backlight indicator-->
<item type="id" name="backlight_icon" />
-</resources>
+ <item type="id" name="keyguard_root_view" />
+ <item type="id" name="keyguard_indication_area" />
+ <item type="id" name="keyguard_indication_text" />
+ <item type="id" name="keyguard_indication_text_bottom" />
+</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
index 9a0044761504..0ca2f7a36311 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt
@@ -263,6 +263,8 @@ constructor(
(colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) !=
WallpaperColors.HINT_SUPPORTS_DARK_TEXT
)
+ if (DEBUG)
+ Log.d(TAG, "onColorsChanged() | region darkness = $regionDarkness for region $area")
updateForegroundColor()
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 8ea4c31a5ac9..84a2c25999a0 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,18 +15,18 @@
*/
package com.android.keyguard
-import android.app.WallpaperManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.text.format.DateFormat
-import android.util.TypedValue
import android.util.Log
+import android.util.TypedValue
import android.view.View
import android.view.View.OnAttachStateChangeListener
import android.view.ViewTreeObserver
+import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -99,6 +99,28 @@ constructor(
if (!regionSamplingEnabled) {
updateColors()
+ } else {
+ clock?.let {
+ smallRegionSampler = createRegionSampler(
+ it.smallClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ )?.apply { startRegionSampler() }
+
+ largeRegionSampler = createRegionSampler(
+ it.largeClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ )?.apply { startRegionSampler() }
+
+ updateColors()
+ }
}
updateFontSizes()
updateTimeListeners()
@@ -110,8 +132,25 @@ constructor(
}
value.smallClock.view.addOnAttachStateChangeListener(
object : OnAttachStateChangeListener {
- override fun onViewAttachedToWindow(p0: View?) {
+ var pastVisibility: Int? = null
+ override fun onViewAttachedToWindow(view: View?) {
value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ if (view != null) {
+ val smallClockFrame = view.parent as FrameLayout
+ pastVisibility = smallClockFrame.visibility
+ smallClockFrame.viewTreeObserver.addOnGlobalLayoutListener(
+ ViewTreeObserver.OnGlobalLayoutListener {
+ val currentVisibility = smallClockFrame.visibility
+ if (pastVisibility != currentVisibility) {
+ pastVisibility = currentVisibility
+ // when small clock visible, recalculate bounds and sample
+ if (currentVisibility == View.VISIBLE) {
+ smallRegionSampler?.stopRegionSampler()
+ smallRegionSampler?.startRegionSampler()
+ }
+ }
+ })
+ }
}
override fun onViewDetachedFromWindow(p0: View?) {
@@ -141,21 +180,19 @@ constructor(
private fun updateColors() {
- val wallpaperManager = WallpaperManager.getInstance(context)
if (regionSamplingEnabled) {
- regionSampler?.let { regionSampler ->
- clock?.let { clock ->
- if (regionSampler.sampledView == clock.smallClock.view) {
- smallClockIsDark = regionSampler.currentRegionDarkness().isDark
- clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
- return@updateColors
- } else if (regionSampler.sampledView == clock.largeClock.view) {
- largeClockIsDark = regionSampler.currentRegionDarkness().isDark
- clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
- return@updateColors
- }
+ clock?.let { clock ->
+ smallRegionSampler?.let {
+ smallClockIsDark = it.currentRegionDarkness().isDark
+ clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
+ }
+
+ largeRegionSampler?.let {
+ largeClockIsDark = it.currentRegionDarkness().isDark
+ clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
}
}
+ return
}
val isLightTheme = TypedValue()
@@ -168,23 +205,6 @@ constructor(
largeClock.events.onRegionDarknessChanged(largeClockIsDark)
}
}
-
- private fun updateRegionSampler(sampledRegion: View) {
- regionSampler?.stopRegionSampler()
- regionSampler =
- createRegionSampler(
- sampledRegion,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- isLockscreen = true,
- ::updateColors
- )
- ?.apply { startRegionSampler() }
-
- updateColors()
- }
-
protected open fun createRegionSampler(
sampledView: View,
mainExecutor: Executor?,
@@ -202,7 +222,10 @@ constructor(
) { updateColors() }
}
- var regionSampler: RegionSampler? = null
+ var smallRegionSampler: RegionSampler? = null
+ private set
+ var largeRegionSampler: RegionSampler? = null
+ private set
var smallTimeListener: TimeListener? = null
var largeTimeListener: TimeListener? = null
val shouldTimeListenerRun: Boolean
@@ -319,7 +342,8 @@ constructor(
configurationController.removeCallback(configListener)
batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
- regionSampler?.stopRegionSampler()
+ smallRegionSampler?.stopRegionSampler()
+ largeRegionSampler?.stopRegionSampler()
smallTimeListener?.stop()
largeTimeListener?.stop()
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 8f17edd23f55..25ad3af52d04 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -531,9 +531,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
if (clock != null) {
clock.dump(pw);
}
- final RegionSampler regionSampler = mClockEventController.getRegionSampler();
- if (regionSampler != null) {
- regionSampler.dump(pw);
+ final RegionSampler smallRegionSampler = mClockEventController.getSmallRegionSampler();
+ if (smallRegionSampler != null) {
+ smallRegionSampler.dump(pw);
+ }
+ final RegionSampler largeRegionSampler = mClockEventController.getLargeRegionSampler();
+ if (largeRegionSampler != null) {
+ largeRegionSampler.dump(pw);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index c684dc54c6fd..c4ebee2a9197 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -77,7 +77,7 @@ class AuthenticationRepositoryImpl @Inject constructor() : AuthenticationReposit
override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
private val _authenticationMethod =
- MutableStateFlow<AuthenticationMethodModel>(AuthenticationMethodModel.PIN(1234))
+ MutableStateFlow<AuthenticationMethodModel>(AuthenticationMethodModel.Pin(1234))
override val authenticationMethod: StateFlow<AuthenticationMethodModel> =
_authenticationMethod.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 3984627a181d..dd9dcbedd6fc 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.authentication.domain.interactor
+import android.app.admin.DevicePolicyManager
import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.SysUISingleton
@@ -129,7 +130,7 @@ constructor(
fun authenticate(input: List<Any>): Boolean {
val isSuccessful =
when (val authMethod = this.authenticationMethod.value) {
- is AuthenticationMethodModel.PIN -> input.asCode() == authMethod.code
+ is AuthenticationMethodModel.Pin -> input.asCode() == authMethod.code
is AuthenticationMethodModel.Password -> input.asPassword() == authMethod.password
is AuthenticationMethodModel.Pattern -> input.asPattern() == authMethod.coordinates
else -> true
@@ -177,15 +178,21 @@ constructor(
/**
* Returns a PIN code from the given list. It's assumed the given list elements are all
- * [Int].
+ * [Int] in the range [0-9].
*/
- private fun List<Any>.asCode(): Int? {
- if (isEmpty()) {
+ private fun List<Any>.asCode(): Long? {
+ if (isEmpty() || size > DevicePolicyManager.MAX_PASSWORD_LENGTH) {
return null
}
- var code = 0
- map { it as Int }.forEach { integer -> code = code * 10 + integer }
+ var code = 0L
+ map {
+ require(it is Int && it in 0..9) {
+ "Pin is required to be Int in range [0..9], but got $it"
+ }
+ it
+ }
+ .forEach { integer -> code = code * 10 + integer }
return code
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index 6f008c3017b9..e4fbf9af35ea 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -32,7 +32,13 @@ sealed class AuthenticationMethodModel(
/** The most basic authentication method. The lock screen can be swiped away when displayed. */
object Swipe : AuthenticationMethodModel(isSecure = false)
- data class PIN(val code: Int) : AuthenticationMethodModel(isSecure = true)
+ /**
+ * Authentication method using a PIN.
+ *
+ * In practice, a pin is restricted to 16 decimal digits , see
+ * [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH]
+ */
+ data class Pin(val code: Long) : AuthenticationMethodModel(isSecure = true)
data class Password(val password: String) : AuthenticationMethodModel(isSecure = true)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 1d2fce7d8b05..a24a421ff2e1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -197,7 +197,7 @@ constructor(
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
return when (authMethod) {
- is AuthenticationMethodModel.PIN ->
+ is AuthenticationMethodModel.Pin ->
applicationContext.getString(R.string.keyguard_enter_your_pin)
is AuthenticationMethodModel.Password ->
applicationContext.getString(R.string.keyguard_enter_your_password)
@@ -209,7 +209,7 @@ constructor(
private fun errorMessage(authMethod: AuthenticationMethodModel): String {
return when (authMethod) {
- is AuthenticationMethodModel.PIN -> applicationContext.getString(R.string.kg_wrong_pin)
+ is AuthenticationMethodModel.Pin -> applicationContext.getString(R.string.kg_wrong_pin)
is AuthenticationMethodModel.Password ->
applicationContext.getString(R.string.kg_wrong_password)
is AuthenticationMethodModel.Pattern ->
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 984d9ab1c1be..527fe6ec847d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -126,7 +126,7 @@ constructor(
.map { model ->
model?.let {
when (interactor.authenticationMethod.value) {
- is AuthenticationMethodModel.PIN ->
+ is AuthenticationMethodModel.Pin ->
R.string.kg_too_many_failed_pin_attempts_dialog_message
is AuthenticationMethodModel.Password ->
R.string.kg_too_many_failed_password_attempts_dialog_message
@@ -165,7 +165,7 @@ constructor(
authMethod: AuthenticationMethodModel,
): AuthMethodBouncerViewModel? {
return when (authMethod) {
- is AuthenticationMethodModel.PIN -> pin
+ is AuthenticationMethodModel.Pin -> pin
is AuthenticationMethodModel.Password -> password
is AuthenticationMethodModel.Pattern -> pattern
else -> null
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 5c0fd92e7299..94d3d193e9cc 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -16,18 +16,10 @@
package com.android.systemui.bouncer.ui.viewmodel
-import androidx.annotation.VisibleForTesting
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.util.kotlin.pairwise
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the PIN code bouncer UI. */
class PinBouncerViewModel(
@@ -39,21 +31,8 @@ class PinBouncerViewModel(
isInputEnabled = isInputEnabled,
) {
- private val entered = MutableStateFlow<List<Int>>(emptyList())
- /**
- * The length of the PIN digits that were input so far, two values are supplied the previous and
- * the current.
- */
- val pinLengths: StateFlow<Pair<Int, Int>> =
- entered
- .pairwise()
- .map { it.previousValue.size to it.newValue.size }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = 0 to 0,
- )
- private var resetPinJob: Job? = null
+ private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList())
+ val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries
/** Notifies that the UI has been shown to the user. */
fun onShown() {
@@ -62,47 +41,48 @@ class PinBouncerViewModel(
/** Notifies that the user clicked on a PIN button with the given digit value. */
fun onPinButtonClicked(input: Int) {
- resetPinJob?.cancel()
- resetPinJob = null
-
- if (entered.value.isEmpty()) {
+ if (mutablePinEntries.value.isEmpty()) {
interactor.clearMessage()
}
- entered.value += input
+ mutablePinEntries.value += EnteredKey(input)
}
/** Notifies that the user clicked the backspace button. */
fun onBackspaceButtonClicked() {
- if (entered.value.isEmpty()) {
+ if (mutablePinEntries.value.isEmpty()) {
return
}
-
- entered.value = entered.value.toMutableList().apply { removeLast() }
+ mutablePinEntries.value = mutablePinEntries.value.toMutableList().apply { removeLast() }
}
/** Notifies that the user long-pressed the backspace button. */
fun onBackspaceButtonLongPressed() {
- resetPinJob?.cancel()
- resetPinJob =
- applicationScope.launch {
- while (entered.value.isNotEmpty()) {
- onBackspaceButtonClicked()
- delay(BACKSPACE_LONG_PRESS_DELAY_MS)
- }
- }
+ mutablePinEntries.value = emptyList()
}
/** Notifies that the user clicked the "enter" button. */
fun onAuthenticateButtonClicked() {
- if (!interactor.authenticate(entered.value)) {
+ if (!interactor.authenticate(mutablePinEntries.value.map { it.input })) {
showFailureAnimation()
}
- entered.value = emptyList()
+ mutablePinEntries.value = emptyList()
}
+}
- companion object {
- @VisibleForTesting const val BACKSPACE_LONG_PRESS_DELAY_MS = 80L
- }
+private var nextSequenceNumber = 1
+
+/**
+ * The pin bouncer [input] as digits 0-9, together with a [sequenceNumber] to indicate the ordering.
+ *
+ * Since the model only allows appending/removing [EnteredKey]s from the end, the [sequenceNumber]
+ * is strictly increasing in input order of the pin, but not guaranteed to be monotonic or start at
+ * a specific number.
+ */
+data class EnteredKey
+internal constructor(val input: Int, val sequenceNumber: Int = nextSequenceNumber++) :
+ Comparable<EnteredKey> {
+ override fun compareTo(other: EnteredKey): Int =
+ compareValuesBy(this, other, EnteredKey::sequenceNumber)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index e60063248fb0..fb19ac99d19e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -217,7 +217,6 @@ open class ControlsProviderSelectorActivity @Inject constructor(
)
}
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
- animateExitAndFinish()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 7cbd1f53612b..be50a1468f07 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -162,7 +162,11 @@ class DetailDialog(
broadcastSender.closeSystemDialogs()
// not sent as interactive, lest the higher-importance activity launch
// be impacted
- pendingIntent.send()
+ val options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle()
+ pendingIntent.send(options)
false
}
if (keyguardStateController.isUnlocked()) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
index 116f3ca898c0..84cda5a541e3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.ui
+import android.app.ActivityOptions
import android.app.AlertDialog
import android.app.PendingIntent
import android.content.DialogInterface
@@ -74,7 +75,11 @@ class StatusBehavior : Behavior {
R.string.controls_open_app,
DialogInterface.OnClickListener { dialog, _ ->
try {
- cws.control?.getAppIntent()?.send()
+ val options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle()
+ cws.control?.getAppIntent()?.send(options)
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
} catch (e: PendingIntent.CanceledException) {
cvh.setErrorStatus()
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 25634f009fc7..76002d3f9693 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -34,6 +34,7 @@ import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.keyboard.KeyboardUI
import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable
import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
import com.android.systemui.log.SessionTracker
import com.android.systemui.media.RingtonePlayer
@@ -295,4 +296,9 @@ abstract class SystemUICoreStartableModule {
@IntoMap
@ClassKey(AssistantAttentionMonitor::class)
abstract fun bindAssistantAttentionMonitor(sysui: AssistantAttentionMonitor): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardViewConfigurator::class)
+ abstract fun bindKeyguardViewConfigurator(impl: KeyguardViewConfigurator): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 870fff75ed3a..432ec8aef5ed 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -134,7 +134,7 @@ object Flags {
// TODO(b/275694445): Tracking Bug
@JvmField
- val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = unreleasedFlag(208,
+ val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = releasedFlag(208,
"lockscreen_without_secure_lock_when_dreaming")
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
new file mode 100644
index 000000000000..05c23aefe974
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.CoreStartable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.statusbar.KeyguardIndicationController
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+/** Binds keyguard views on startup, and also exposes methods to allow rebinding if views change */
+@SysUISingleton
+class KeyguardViewConfigurator
+@Inject
+constructor(
+ private val keyguardRootView: KeyguardRootView,
+ private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
+ private val notificationShadeWindowView: NotificationShadeWindowView,
+ private val featureFlags: FeatureFlags,
+ private val indicationController: KeyguardIndicationController,
+) : CoreStartable {
+
+ private var indicationAreaHandle: DisposableHandle? = null
+
+ override fun start() {
+ bindIndicationArea(
+ notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
+ )
+ }
+
+ fun bindIndicationArea(legacyParent: ViewGroup) {
+ indicationAreaHandle?.dispose()
+
+ // At startup, 2 views with the ID `R.id.keyguard_indication_area` will be available.
+ // Disable one of them
+ if (featureFlags.isEnabled(Flags.MIGRATE_INDICATION_AREA)) {
+ legacyParent.requireViewById<View>(R.id.keyguard_indication_area).let {
+ legacyParent.removeView(it)
+ }
+ } else {
+ keyguardRootView.findViewById<View?>(R.id.keyguard_indication_area)?.let {
+ keyguardRootView.removeView(it)
+ }
+ }
+
+ indicationAreaHandle =
+ KeyguardIndicationAreaBinder.bind(
+ notificationShadeWindowView,
+ keyguardIndicationAreaViewModel,
+ indicationController
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 3cf9a9ec5a9c..f99b8a2458f2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -21,6 +21,7 @@ import android.app.StatusBarManager
import android.graphics.Point
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -59,6 +60,7 @@ constructor(
private val commandQueue: CommandQueue,
featureFlags: FeatureFlags,
bouncerRepository: KeyguardBouncerRepository,
+ configurationRepository: ConfigurationRepository,
) {
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
@@ -172,6 +174,9 @@ constructor(
/** The approximate location on the screen of the face unlock sensor, if one is available. */
val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
+ /** Notifies when a new configuration is set */
+ val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange
+
fun dozeTransitionTo(vararg states: DozeStateModel): Flow<DozeTransitionModel> {
return dozeTransitionModel.filter { states.contains(it.to) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index a8d662c96284..7d14198bdb17 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -21,12 +21,10 @@ import android.content.Intent
import android.graphics.Rect
import android.graphics.drawable.Animatable2
import android.util.Size
-import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.widget.ImageView
-import android.widget.TextView
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@@ -108,14 +106,10 @@ object KeyguardBottomAreaViewBinder {
activityStarter: ActivityStarter?,
messageDisplayer: (Int) -> Unit,
): Binding {
- val indicationArea: View = view.requireViewById(R.id.keyguard_indication_area)
val ambientIndicationArea: View? = view.findViewById(R.id.ambient_indication_container)
val startButton: ImageView = view.requireViewById(R.id.start_button)
val endButton: ImageView = view.requireViewById(R.id.end_button)
val overlayContainer: View = view.requireViewById(R.id.overlay_container)
- val indicationText: TextView = view.requireViewById(R.id.keyguard_indication_text)
- val indicationTextBottom: TextView =
- view.requireViewById(R.id.keyguard_indication_text_bottom)
val settingsMenu: LaunchableLinearLayout =
view.requireViewById(R.id.keyguard_settings_button)
@@ -183,7 +177,6 @@ object KeyguardBottomAreaViewBinder {
}
ambientIndicationArea?.alpha = alpha
- indicationArea.alpha = alpha
}
}
@@ -205,50 +198,23 @@ object KeyguardBottomAreaViewBinder {
launch {
viewModel.indicationAreaTranslationX.collect { translationX ->
- indicationArea.translationX = translationX
ambientIndicationArea?.translationX = translationX
}
}
launch {
- combine(
- viewModel.isIndicationAreaPadded,
- configurationBasedDimensions.map { it.indicationAreaPaddingPx },
- ) { isPadded, paddingIfPaddedPx ->
- if (isPadded) {
- paddingIfPaddedPx
- } else {
- 0
- }
- }
- .collect { paddingPx ->
- indicationArea.setPadding(paddingPx, 0, paddingPx, 0)
- }
- }
-
- launch {
configurationBasedDimensions
.map { it.defaultBurnInPreventionYOffsetPx }
.flatMapLatest { defaultBurnInOffsetY ->
viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
}
.collect { translationY ->
- indicationArea.translationY = translationY
ambientIndicationArea?.translationY = translationY
}
}
launch {
configurationBasedDimensions.collect { dimensions ->
- indicationText.setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- dimensions.indicationTextSizePx.toFloat(),
- )
- indicationTextBottom.setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- dimensions.indicationTextSizePx.toFloat(),
- )
-
startButton.updateLayoutParams<ViewGroup.LayoutParams> {
width = dimensions.buttonSizePx.width
height = dimensions.buttonSizePx.height
@@ -305,7 +271,7 @@ object KeyguardBottomAreaViewBinder {
return object : Binding {
override fun getIndicationAreaAnimators(): List<ViewPropertyAnimator> {
- return listOf(indicationArea, ambientIndicationArea).mapNotNull { it?.animate() }
+ return listOf(ambientIndicationArea).mapNotNull { it?.animate() }
}
override fun onConfigurationChanged() {
@@ -517,12 +483,6 @@ object KeyguardBottomAreaViewBinder {
return ConfigurationBasedDimensions(
defaultBurnInPreventionYOffsetPx =
view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset),
- indicationAreaPaddingPx =
- view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding),
- indicationTextSizePx =
- view.resources.getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material,
- ),
buttonSizePx =
Size(
view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
@@ -552,8 +512,6 @@ object KeyguardBottomAreaViewBinder {
private data class ConfigurationBasedDimensions(
val defaultBurnInPreventionYOffsetPx: Int,
- val indicationAreaPaddingPx: Int,
- val indicationTextSizePx: Int,
val buttonSizePx: Size,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
new file mode 100644
index 000000000000..02e6765fa1d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.ui.binder
+
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.KeyguardIndicationController
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/**
+ * Binds a keyguard indication area view to its view-model.
+ *
+ * To use this properly, users should maintain a one-to-one relationship between the [View] and the
+ * view-binding, binding each view only once. It is okay and expected for the same instance of the
+ * view-model to be reused for multiple view/view-binder bindings.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+object KeyguardIndicationAreaBinder {
+
+ /** Binds the view to the view-model, continuing to update the former based on the latter. */
+ @JvmStatic
+ fun bind(
+ view: ViewGroup,
+ viewModel: KeyguardIndicationAreaViewModel,
+ indicationController: KeyguardIndicationController,
+ ): DisposableHandle {
+ val indicationArea: ViewGroup = view.requireViewById(R.id.keyguard_indication_area)
+ indicationController.setIndicationArea(indicationArea)
+
+ val indicationText: TextView = indicationArea.requireViewById(R.id.keyguard_indication_text)
+ val indicationTextBottom: TextView =
+ indicationArea.requireViewById(R.id.keyguard_indication_text_bottom)
+
+ view.clipChildren = false
+ view.clipToPadding = false
+
+ val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
+ val disposableHandle =
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.alpha.collect { alpha ->
+ view.importantForAccessibility =
+ if (alpha == 0f) {
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ } else {
+ View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ }
+
+ indicationArea.alpha = alpha
+ }
+ }
+
+ launch {
+ viewModel.indicationAreaTranslationX.collect { translationX ->
+ indicationArea.translationX = translationX
+ }
+ }
+
+ launch {
+ combine(
+ viewModel.isIndicationAreaPadded,
+ configurationBasedDimensions.map { it.indicationAreaPaddingPx },
+ ) { isPadded, paddingIfPaddedPx ->
+ if (isPadded) {
+ paddingIfPaddedPx
+ } else {
+ 0
+ }
+ }
+ .collect { paddingPx ->
+ indicationArea.setPadding(paddingPx, 0, paddingPx, 0)
+ }
+ }
+
+ launch {
+ configurationBasedDimensions
+ .map { it.defaultBurnInPreventionYOffsetPx }
+ .flatMapLatest { defaultBurnInOffsetY ->
+ viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
+ }
+ .collect { translationY -> indicationArea.translationY = translationY }
+ }
+
+ launch {
+ configurationBasedDimensions.collect { dimensions ->
+ indicationText.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ dimensions.indicationTextSizePx.toFloat(),
+ )
+ indicationTextBottom.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ dimensions.indicationTextSizePx.toFloat(),
+ )
+ }
+ }
+
+ launch {
+ viewModel.configurationChange.collect {
+ configurationBasedDimensions.value = loadFromResources(view)
+ }
+ }
+ }
+ }
+ return disposableHandle
+ }
+
+ private fun loadFromResources(view: View): ConfigurationBasedDimensions {
+ return ConfigurationBasedDimensions(
+ defaultBurnInPreventionYOffsetPx =
+ view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset),
+ indicationAreaPaddingPx =
+ view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding),
+ indicationTextSizePx =
+ view.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.text_size_small_material,
+ ),
+ )
+ }
+
+ private data class ConfigurationBasedDimensions(
+ val defaultBurnInPreventionYOffsetPx: Int,
+ val indicationAreaPaddingPx: Int,
+ val indicationTextSizePx: Int,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index fe62bf4388e9..db231092d7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.ui.preview
-import android.annotation.ColorInt
+import android.app.WallpaperColors
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -47,6 +47,7 @@ import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBind
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
+import com.android.systemui.monet.ColorScheme
import com.android.systemui.plugins.ClockController
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.clocks.DefaultClockController
@@ -91,6 +92,7 @@ constructor(
/** [shouldHideClock] here means that we never create and bind the clock views */
private val shouldHideClock: Boolean =
bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
+ private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS)
private var host: SurfaceControlViewHost
@@ -100,7 +102,6 @@ constructor(
private lateinit var largeClockHostView: FrameLayout
private lateinit var smallClockHostView: FrameLayout
private var smartSpaceView: View? = null
- private var colorOverride: Int? = null
private val disposables = mutableSetOf<DisposableHandle>()
private var isDestroyed = false
@@ -172,6 +173,10 @@ constructor(
rootView.translationX = (width - scale * rootView.width) / 2
rootView.translationY = (height - scale * rootView.height) / 2
+ if (isDestroyed) {
+ return@post
+ }
+
host.setView(rootView, rootView.measuredWidth, rootView.measuredHeight)
}
}
@@ -195,14 +200,6 @@ constructor(
mainHandler.post { smartSpaceView?.visibility = if (hide) View.INVISIBLE else View.VISIBLE }
}
- /** Sets the clock's color to the overridden seed color. */
- fun onColorOverridden(@ColorInt color: Int?) {
- mainHandler.post {
- colorOverride = color
- clockController.clock?.run { events.onSeedColorChanged(color) }
- }
- }
-
/**
* This sets up and shows a non-interactive smart space
*
@@ -395,7 +392,24 @@ constructor(
val clock = clockRegistry.createCurrentClock()
clockController.clock = clock
- colorOverride?.let { clock.events.onSeedColorChanged(it) }
+ if (clockRegistry.seedColor == null) {
+ // Seed color null means users do override any color on the clock. The default color
+ // will need to use wallpaper's extracted color and consider if the wallpaper's color
+ // is dark or a light.
+ // TODO(b/277832214) we can potentially simplify this code by checking for
+ // wallpaperColors being null in the if clause above and removing the many ?.
+ val wallpaperColorScheme =
+ wallpaperColors?.let { ColorScheme(it, /* darkTheme= */ false) }
+ val lightClockColor = wallpaperColorScheme?.accent1?.s100
+ val darkClockColor = wallpaperColorScheme?.accent2?.s600
+ /** Note that when [wallpaperColors] is null, isWallpaperDark is true. */
+ val isWallpaperDark: Boolean =
+ (wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) !=
+ WallpaperColors.HINT_SUPPORTS_DARK_TEXT
+ clock.events.onSeedColorChanged(
+ if (isWallpaperDark) lightClockColor else darkClockColor
+ )
+ }
updateLargeClock(clock)
updateSmallClock(clock)
@@ -428,6 +442,7 @@ constructor(
private const val KEY_VIEW_WIDTH = "width"
private const val KEY_VIEW_HEIGHT = "height"
private const val KEY_DISPLAY_ID = "display_id"
+ private const val KEY_COLORS = "wallpaper_colors"
private const val DIM_ALPHA = 0.3f
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
index 79712f9c6be5..dafeacef60bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
@@ -124,13 +124,6 @@ constructor(
message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE)
)
}
- KeyguardPreviewConstants.MESSAGE_ID_COLOR_OVERRIDE -> {
- renderer.onColorOverridden(
- message.data
- .getString(KeyguardPreviewConstants.KEY_COLOR_OVERRIDE)
- ?.toIntOrNull()
- )
- }
else -> requestDestruction(this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
new file mode 100644
index 000000000000..890d565b03a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.ui.view
+
+import android.content.Context
+import android.text.TextUtils
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.LinearLayout
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+
+class KeyguardIndicationArea(
+ context: Context,
+ private val attrs: AttributeSet?,
+) :
+ LinearLayout(
+ context,
+ attrs,
+ ) {
+
+ init {
+ setId(R.id.keyguard_indication_area)
+ orientation = LinearLayout.VERTICAL
+
+ addView(indicationTopRow(), LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT))
+ addView(
+ indicationBottomRow(),
+ LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
+ gravity = Gravity.CENTER_HORIZONTAL
+ }
+ )
+ }
+
+ private fun indicationTopRow(): KeyguardIndicationTextView {
+ return KeyguardIndicationTextView(context, attrs).apply {
+ id = R.id.keyguard_indication_text
+ gravity = Gravity.CENTER
+ accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE
+ setTextAppearance(R.style.TextAppearance_Keyguard_BottomArea)
+
+ val padding = R.dimen.keyguard_indication_text_padding.dp()
+ setPaddingRelative(padding, 0, padding, 0)
+ }
+ }
+
+ private fun indicationBottomRow(): KeyguardIndicationTextView {
+ return KeyguardIndicationTextView(context, attrs).apply {
+ id = R.id.keyguard_indication_text_bottom
+ gravity = Gravity.CENTER
+ accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE
+
+ setTextAppearance(R.style.TextAppearance_Keyguard_BottomArea)
+ setEllipsize(TextUtils.TruncateAt.END)
+ setAlpha(0.8f)
+ setMinHeight(R.dimen.keyguard_indication_text_min_height.dp())
+ setMaxLines(2)
+ setVisibility(View.GONE)
+
+ val padding = R.dimen.keyguard_indication_text_padding.dp()
+ setPaddingRelative(padding, 0, padding, 0)
+ }
+ }
+
+ private fun Int.dp(): Int {
+ return context.resources.getDimensionPixelSize(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
new file mode 100644
index 000000000000..abf0e80bd87a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+/** Provides a container for all keyguard ui content. */
+class KeyguardRootView(
+ context: Context,
+ private val attrs: AttributeSet?,
+) :
+ FrameLayout(
+ context,
+ attrs,
+ ) {
+
+ init {
+ addIndicationTextArea()
+ }
+
+ private fun addIndicationTextArea() {
+ val view = KeyguardIndicationArea(context, attrs)
+ addView(
+ view,
+ FrameLayout.LayoutParams(
+ MATCH_PARENT,
+ WRAP_CONTENT,
+ )
+ .apply {
+ gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ bottomMargin = R.dimen.keyguard_indication_margin_bottom.dp()
+ }
+ )
+ }
+
+ private fun Int.dp(): Int {
+ return context.resources.getDimensionPixelSize(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 62a1a9e16fc6..3e6f8e68891a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -57,7 +57,7 @@ constructor(
* in the wallpaper picker application. This should _always_ be `false` for the real lock screen
* experience.
*/
- private val previewMode = MutableStateFlow(PreviewMode())
+ val previewMode = MutableStateFlow(PreviewMode())
/**
* ID of the slot that's currently selected in the preview that renders exclusively in the
@@ -101,12 +101,6 @@ constructor(
bottomAreaInteractor.alpha.distinctUntilChanged()
}
}
- /** An observable for whether the indication area should be padded. */
- val isIndicationAreaPadded: Flow<Boolean> =
- combine(startButton, endButton) { startButtonModel, endButtonModel ->
- startButtonModel.isVisible || endButtonModel.isVisible
- }
- .distinctUntilChanged()
/** An observable for the x-offset by which the indication area should be translated. */
val indicationAreaTranslationX: Flow<Float> =
bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
new file mode 100644
index 000000000000..389cf76c47ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/** View-model for the keyguard indication area view */
+@OptIn(ExperimentalCoroutinesApi::class)
+class KeyguardIndicationAreaViewModel
+@Inject
+constructor(
+ private val keyguardInteractor: KeyguardInteractor,
+ private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
+ private val keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
+ private val burnInHelperWrapper: BurnInHelperWrapper,
+) {
+
+ /** Notifies when a new configuration is set */
+ val configurationChange: Flow<Unit> = keyguardInteractor.configurationChange
+
+ /** An observable for the alpha level for the entire bottom area. */
+ val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
+
+ /** An observable for whether the indication area should be padded. */
+ val isIndicationAreaPadded: Flow<Boolean> =
+ combine(keyguardBottomAreaViewModel.startButton, keyguardBottomAreaViewModel.endButton) {
+ startButtonModel,
+ endButtonModel ->
+ startButtonModel.isVisible || endButtonModel.isVisible
+ }
+ .distinctUntilChanged()
+ /** An observable for the x-offset by which the indication area should be translated. */
+ val indicationAreaTranslationX: Flow<Float> =
+ bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
+
+ /** Returns an observable for the y-offset by which the indication area should be translated. */
+ fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> {
+ return keyguardInteractor.dozeAmount
+ .map { dozeAmount ->
+ dozeAmount *
+ (burnInHelperWrapper.burnInOffset(
+ /* amplitude = */ defaultBurnInOffset * 2,
+ /* xAxis= */ false,
+ ) - defaultBurnInOffset)
+ }
+ .distinctUntilChanged()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 790df0246bc1..1174d3d932ae 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -125,6 +125,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.KeyguardViewConfigurator;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
@@ -598,6 +599,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final KeyguardInteractor mKeyguardInteractor;
+ private final KeyguardViewConfigurator mKeyguardViewConfigurator;
private final @Nullable MultiShadeInteractor mMultiShadeInteractor;
private final CoroutineDispatcher mMainDispatcher;
private boolean mIsAnyMultiShadeExpanded;
@@ -740,6 +742,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
KeyguardLongPressViewModel keyguardLongPressViewModel,
KeyguardInteractor keyguardInteractor,
ActivityStarter activityStarter,
+ KeyguardViewConfigurator keyguardViewConfigurator,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
mInteractionJankMonitor = interactionJankMonitor;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@@ -762,6 +765,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mKeyguardInteractor = keyguardInteractor;
+ mKeyguardViewConfigurator = keyguardViewConfigurator;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -1319,7 +1323,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardBottomArea.initFrom(oldBottomArea);
mView.addView(mKeyguardBottomArea, index);
initBottomArea();
- mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
mStatusBarStateController.getInterpolatedDozeAmount());
@@ -1361,6 +1364,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardIndicationController.showTransientIndication(stringResourceId),
mVibratorHelper,
mActivityStarter);
+
+ // Rebind (for now), as a new bottom area and indication area may have been created
+ mKeyguardViewConfigurator.bindIndicationArea(mKeyguardBottomArea);
}
@VisibleForTesting
@@ -1398,7 +1404,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private void setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea) {
mKeyguardBottomArea = keyguardBottomArea;
- mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
}
void setOpenCloseListener(OpenCloseListener openCloseListener) {
@@ -3023,12 +3028,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardStatusViewController.setStatusAccessibilityImportance(mode);
}
- //TODO(b/254875405): this should be removed.
- @Override
- public KeyguardBottomAreaView getKeyguardBottomAreaView() {
- return mKeyguardBottomArea;
- }
-
@Override
public void applyLaunchAnimationProgress(float linearProgress) {
boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 44c732d8082f..1752ff6d531f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -32,6 +32,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.settings.UserTracker
@@ -71,9 +72,9 @@ abstract class ShadeModule {
layoutInflater.inflate(R.layout.scene_window_root, null)
} else {
layoutInflater.inflate(R.layout.super_notification_shade, null)
- } as WindowRootView? ?: throw IllegalStateException(
- "Window root view could not be properly inflated"
- )
+ }
+ as WindowRootView?
+ ?: throw IllegalStateException("Window root view could not be properly inflated")
}
@Provides
@@ -89,9 +90,7 @@ abstract class ShadeModule {
return root.findViewById(R.id.legacy_window_root)
}
return root as NotificationShadeWindowView?
- ?: throw IllegalStateException(
- "root view not a NotificationShadeWindowView"
- )
+ ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@@ -120,6 +119,14 @@ abstract class ShadeModule {
return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
}
+ @Provides
+ @SysUISingleton
+ fun providesKeyguardRootView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): KeyguardRootView {
+ return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
+ }
+
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index f75047c2072a..203355e71021 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -21,7 +21,6 @@ import com.android.systemui.statusbar.RemoteInputController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
import com.android.systemui.statusbar.phone.KeyguardStatusBarView
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController
import java.util.function.Consumer
@@ -120,13 +119,6 @@ interface ShadeViewController {
/** Returns the StatusBarState. */
val barState: Int
- /**
- * Returns the bottom part of the keyguard, which contains quick affordances.
- *
- * TODO(b/275550429): this should be removed.
- */
- val keyguardBottomAreaView: KeyguardBottomAreaView?
-
/** Returns the NSSL controller. */
val notificationStackScrollLayoutController: NotificationStackScrollLayoutController
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
index ba9d13d57a74..6d951bf4d83b 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.smartspace.dagger
+import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
import android.view.View
@@ -81,7 +82,11 @@ interface SmartspaceViewComponent {
showOnLockscreen: Boolean
) {
if (showOnLockscreen) {
- pi.send()
+ val options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle()
+ pi.send(options)
} else {
activityStarter.startPendingIntentDismissingKeyguard(pi)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 7cc917f3b0b8..518825cea5e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.lockscreen
+import android.app.ActivityOptions
import android.app.PendingIntent
import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
@@ -358,7 +359,11 @@ constructor(
showOnLockscreen: Boolean
) {
if (showOnLockscreen) {
- pi.send()
+ val options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ .toBundle()
+ pi.send(options)
} else {
activityStarter.postStartActivityDismissingKeyguard(pi)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 0159d1274e85..5ad5d841fad5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -574,6 +574,9 @@ constructor(
// TODO b/221255671: restrict this to only be set for
// notifications
options.isEligibleForLegacyPermissionPrompt = true
+ options.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ )
return intent.sendAndReturnResult(
null,
0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index d759e58f073e..0b02f33daeae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -31,7 +31,6 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.View;
-import android.view.ViewGroup;
import android.window.RemoteTransition;
import android.window.SplashScreen;
@@ -40,7 +39,6 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -50,7 +48,6 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.statusbar.LightRevealScrim;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Compile;
@@ -68,14 +65,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
String TAG = "CentralSurfaces";
boolean DEBUG = false;
boolean SPEW = false;
- boolean DUMPTRUCK = true; // extra dumpsys info
boolean DEBUG_GESTURES = false;
boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
boolean DEBUG_CAMERA_LIFT = false;
boolean DEBUG_WINDOW_STATE = false;
boolean DEBUG_WAKEUP_DELAY = Compile.IS_DEBUG;
- // additional instrumentation for testing purposes; intended to be left on during development
- boolean CHATTY = DEBUG;
boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
String ACTION_FAKE_ARTWORK = "fake_artwork";
int FADE_KEYGUARD_START_DELAY = 100;
@@ -234,8 +228,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
void onKeyguardViewManagerStatesUpdated();
- ViewGroup getNotificationScrollLayout();
-
boolean isPulsing();
boolean isOccluded();
@@ -297,8 +289,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
@Override
void dump(PrintWriter pwOriginal, String[] args);
- void createAndAddWindows(@Nullable RegisterStatusBarResult result);
-
float getDisplayWidth();
float getDisplayHeight();
@@ -348,8 +338,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
Runnable cancelAction);
- LightRevealScrim getLightRevealScrim();
-
// TODO: Figure out way to remove these.
NavigationBarView getNavigationBarView();
@@ -463,8 +451,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
void extendDozePulse();
- boolean shouldDelayWakeUpAnimation();
-
public static class KeyboardShortcutsMessage {
final int mDeviceId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 021ef918243e..fde08bde5d17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1816,11 +1816,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
- public ViewGroup getNotificationScrollLayout() {
- return mStackScroller;
- }
-
- @Override
public boolean isPulsing() {
return mDozeServiceHost.isPulsing();
}
@@ -2244,8 +2239,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
+ CameraIntents.getOverrideCameraPackage(mContext));
}
- @Override
- public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
+ private void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
mStatusBarWindowController.attach();
@@ -3026,11 +3020,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
- @Override
- public LightRevealScrim getLightRevealScrim() {
- return mLightRevealScrim;
- }
-
// TODO: Figure out way to remove these.
@Override
public NavigationBarView getNavigationBarView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index dfaee4c674ad..5624e28204cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -40,7 +40,6 @@ import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -86,7 +85,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
- private final KeyguardIndicationController mKeyguardIndicationController;
private final CentralSurfaces mCentralSurfaces;
private final LockscreenShadeTransitionController mShadeTransitionController;
private final CommandQueue mCommandQueue;
@@ -115,7 +113,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
NotificationShadeWindowController notificationShadeWindowController,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
- KeyguardIndicationController keyguardIndicationController,
CentralSurfaces centralSurfaces,
LockscreenShadeTransitionController shadeTransitionController,
CommandQueue commandQueue,
@@ -137,7 +134,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
mQsController = quickSettingsController;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
- mKeyguardIndicationController = keyguardIndicationController;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mCentralSurfaces = centralSurfaces;
mShadeTransitionController = shadeTransitionController;
@@ -173,7 +169,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
mNotificationPanel.getShadeNotificationPresenter().createRemoteInputDelegate());
initController.addPostInitTask(() -> {
- mKeyguardIndicationController.init();
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
index 57b9f914d4d6..ae4820837ce5 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
@@ -18,10 +18,11 @@ package com.android.systemui.theme
import android.util.Pair
import com.android.systemui.monet.dynamiccolor.DynamicColor
-import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors as MDC
+import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors
class DynamicColors {
companion object {
+ private val MDC = MaterialDynamicColors()
@JvmField
val ALL_DYNAMIC_COLORS_MAPPED: List<Pair<String, DynamicColor>> =
arrayListOf(
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c1999b284553..b78329cfa5aa 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -659,6 +659,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
Resources res = userHandle.isSystem()
? mResources : mContext.createContextAsUser(userHandle, 0).getResources();
Resources.Theme theme = mContext.getTheme();
+ MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
if (!(res.getColor(android.R.color.system_accent1_500, theme)
== mColorScheme.getAccent1().getS500()
&& res.getColor(android.R.color.system_accent2_500, theme)
@@ -670,15 +671,15 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
&& res.getColor(android.R.color.system_neutral2_500, theme)
== mColorScheme.getNeutral2().getS500()
&& res.getColor(android.R.color.system_outline_variant_dark, theme)
- == MaterialDynamicColors.outlineVariant().getArgb(mDynamicSchemeDark)
+ == dynamicColors.outlineVariant().getArgb(mDynamicSchemeDark)
&& res.getColor(android.R.color.system_outline_variant_light, theme)
- == MaterialDynamicColors.outlineVariant().getArgb(mDynamicSchemeLight)
+ == dynamicColors.outlineVariant().getArgb(mDynamicSchemeLight)
&& res.getColor(android.R.color.system_primary_container_dark, theme)
- == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeDark)
+ == dynamicColors.primaryContainer().getArgb(mDynamicSchemeDark)
&& res.getColor(android.R.color.system_primary_container_light, theme)
- == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeLight)
+ == dynamicColors.primaryContainer().getArgb(mDynamicSchemeLight)
&& res.getColor(android.R.color.system_primary_fixed, theme)
- == MaterialDynamicColors.primaryFixed().getArgb(mDynamicSchemeLight))) {
+ == dynamicColors.primaryFixed().getArgb(mDynamicSchemeLight))) {
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index dfc6392404a4..cab47a3c4b4b 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -307,6 +307,8 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
+ options.setPendingIntentBackgroundActivityStartMode(
+ BroadcastOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
walletCard.getPendingIntent().send(options.toBundle());
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Error sending pending intent for wallet card.");
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 3f1560bc5fce..8c71392caeec 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -53,6 +53,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
@@ -62,6 +63,7 @@ import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import java.util.TimeZone
import java.util.concurrent.Executor
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@@ -117,6 +119,7 @@ class ClockEventControllerTest : SysuiTestCase() {
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
+ configurationRepository = FakeConfigurationRepository(),
),
KeyguardTransitionInteractor(repository = transitionRepository),
broadcastDispatcher,
@@ -155,9 +158,8 @@ class ClockEventControllerTest : SysuiTestCase() {
@Test
fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) {
- // TODO(b/266103601): delete this test and add more coverage for updateColors()
- // verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
- // verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
+ verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
+ verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 456702b65091..bdf6bee8ecff 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -18,6 +18,7 @@ package com.android.keyguard;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
+import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -42,15 +43,12 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -91,10 +89,9 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
protected @Mock ConfigurationController mConfigurationController;
protected @Mock VibratorHelper mVibrator;
protected @Mock AuthRippleController mAuthRippleController;
- protected @Mock FeatureFlags mFeatureFlags;
protected @Mock KeyguardTransitionRepository mTransitionRepository;
- protected @Mock CommandQueue mCommandQueue;
protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
+ protected FakeFeatureFlags mFeatureFlags;
protected LockIconViewController mUnderTest;
@@ -144,6 +141,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ mFeatureFlags = new FakeFeatureFlags();
+ mFeatureFlags.set(FACE_AUTH_REFACTOR, false);
mUnderTest = new LockIconViewController(
mLockIconView,
mStatusBarStateController,
@@ -160,12 +159,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
mAuthRippleController,
mResources,
new KeyguardTransitionInteractor(mTransitionRepository),
- new KeyguardInteractor(
- new FakeKeyguardRepository(),
- mCommandQueue,
- mFeatureFlags,
- new FakeKeyguardBouncerRepository()
- ),
+ KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags
);
}
@@ -226,7 +220,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
}
protected void init(boolean useMigrationFlag) {
- when(mFeatureFlags.isEnabled(DOZING_MIGRATION_1)).thenReturn(useMigrationFlag);
+ mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag);
mUnderTest.init();
verify(mLockIconView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 1990c8f644b4..3a93e7744d00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.authentication.domain.interactor
+import android.app.admin.DevicePolicyManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.AuthenticationRepository
@@ -48,7 +49,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
fun authMethod() =
testScope.runTest {
val authMethod by collectLastValue(underTest.authenticationMethod)
- assertThat(authMethod).isEqualTo(AuthenticationMethodModel.PIN(1234))
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin(1234))
underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password"))
assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password("password"))
@@ -147,7 +148,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
- underTest.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
@@ -160,7 +161,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
- underTest.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse()
@@ -169,6 +170,51 @@ class AuthenticationInteractorTest : SysuiTestCase() {
}
@Test
+ fun authenticate_withEmptyPin_returnsFalseAndDoesNotUnlockDevice() =
+ testScope.runTest {
+ val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
+ assertThat(isUnlocked).isFalse()
+
+ assertThat(underTest.authenticate(listOf())).isFalse()
+ assertThat(isUnlocked).isFalse()
+ assertThat(failedAttemptCount).isEqualTo(1)
+ }
+
+ @Test
+ fun authenticate_withCorrectMaxLengthPin_returnsTrueAndUnlocksDevice() =
+ testScope.runTest {
+ val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(9999999999999999))
+ assertThat(isUnlocked).isFalse()
+
+ assertThat(underTest.authenticate(List(16) { 9 })).isTrue()
+ assertThat(isUnlocked).isTrue()
+ assertThat(failedAttemptCount).isEqualTo(0)
+ }
+
+ @Test
+ fun authenticate_withCorrectTooLongPin_returnsFalseAndDoesNotUnlockDevice() =
+ testScope.runTest {
+ // Max pin length is 16 digits. To avoid issues with overflows, this test ensures
+ // that all pins > 16 decimal digits are rejected.
+
+ // If the policy changes, there is work to do in SysUI.
+ assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)
+
+ val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(99999999999999999))
+ assertThat(isUnlocked).isFalse()
+
+ assertThat(underTest.authenticate(List(17) { 9 })).isFalse()
+ assertThat(isUnlocked).isFalse()
+ assertThat(failedAttemptCount).isEqualTo(1)
+ }
+
+ @Test
fun authenticate_withCorrectPassword_returnsTrueAndUnlocksDevice() =
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 374c28d6dce8..6a63c32f2c40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -69,7 +69,7 @@ class BouncerInteractorTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
val message by collectLastValue(underTest.message)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
underTest.showOrUnlockDevice("container1")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -167,7 +167,7 @@ class BouncerInteractorTest : SysuiTestCase() {
fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
@@ -211,7 +211,7 @@ class BouncerInteractorTest : SysuiTestCase() {
val throttling by collectLastValue(underTest.throttling)
val message by collectLastValue(underTest.message)
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(throttling).isNull()
assertThat(message).isEqualTo("")
assertThat(isUnlocked).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 1642410e5a3f..b53e03419df3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -55,7 +55,7 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
@Test
fun animateFailure() =
testScope.runTest {
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
val animateFailure by collectLastValue(underTest.animateFailure)
assertThat(animateFailure).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index e8c946cdd59d..c6074962ee8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -97,7 +97,7 @@ class BouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val message by collectLastValue(underTest.message)
val throttling by collectLastValue(bouncerInteractor.throttling)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(message?.isUpdateAnimated).isTrue()
repeat(BouncerInteractor.THROTTLE_EVERY) {
@@ -120,7 +120,7 @@ class BouncerViewModelTest : SysuiTestCase() {
}
)
val throttling by collectLastValue(bouncerInteractor.throttling)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isInputEnabled).isTrue()
repeat(BouncerInteractor.THROTTLE_EVERY) {
@@ -137,7 +137,7 @@ class BouncerViewModelTest : SysuiTestCase() {
fun throttlingDialogMessage() =
testScope.runTest {
val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
repeat(BouncerInteractor.THROTTLE_EVERY) {
// Wrong PIN.
@@ -154,7 +154,7 @@ class BouncerViewModelTest : SysuiTestCase() {
return listOf(
AuthenticationMethodModel.None,
AuthenticationMethodModel.Swipe,
- AuthenticationMethodModel.PIN(1234),
+ AuthenticationMethodModel.Pin(1234),
AuthenticationMethodModel.Password("password"),
AuthenticationMethodModel.Pattern(
listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 3bdaf0590888..7b6bb37459a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -25,12 +25,12 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
+import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -85,8 +85,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -95,7 +95,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onShown()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN)
- assertThat(pinLengths).isEqualTo(0 to 0)
+ assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -106,8 +106,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -117,7 +117,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onPinButtonClicked(1)
assertThat(message?.text).isEmpty()
- assertThat(pinLengths).isEqualTo(0 to 1)
+ assertThat(entries).hasSize(1)
+ assertThat(entries?.map { it.input }).containsExactly(1)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -128,32 +129,59 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
- assertThat(pinLengths).isEqualTo(0 to 1)
+ assertThat(entries).hasSize(1)
underTest.onBackspaceButtonClicked()
assertThat(message?.text).isEmpty()
- assertThat(pinLengths).isEqualTo(1 to 0)
+ assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
+ fun onPinEdit() =
+ testScope.runTest {
+ val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
+ val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
+ val message by collectLastValue(bouncerViewModel.message)
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
+ authenticationInteractor.lockDevice()
+ sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ underTest.onShown()
+
+ underTest.onPinButtonClicked(1)
+ underTest.onPinButtonClicked(2)
+ underTest.onPinButtonClicked(3)
+ underTest.onBackspaceButtonClicked()
+ underTest.onBackspaceButtonClicked()
+ underTest.onPinButtonClicked(4)
+ underTest.onPinButtonClicked(5)
+
+ assertThat(entries).hasSize(3)
+ assertThat(entries?.map { it.input }).containsExactly(1, 4, 5).inOrder()
+ assertThat(entries?.map { it.sequenceNumber }).isInStrictOrder()
+ }
+
+ @Test
fun onBackspaceButtonLongPressed() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -165,13 +193,9 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onPinButtonClicked(4)
underTest.onBackspaceButtonLongPressed()
- repeat(4) { index ->
- assertThat(pinLengths).isEqualTo(4 - index to 3 - index)
- advanceTimeBy(PinBouncerViewModel.BACKSPACE_LONG_PRESS_DELAY_MS)
- }
assertThat(message?.text).isEmpty()
- assertThat(pinLengths).isEqualTo(1 to 0)
+ assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -181,7 +205,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -204,8 +228,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -219,7 +243,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateButtonClicked()
- assertThat(pinLengths).isEqualTo(0 to 0)
+ assertThat(entries).hasSize(0)
assertThat(message?.text).isEqualTo(WRONG_PIN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -231,8 +255,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
- val pinLengths by collectLastValue(underTest.pinLengths)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val entries by collectLastValue(underTest.pinEntries)
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -245,7 +269,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onPinButtonClicked(5) // PIN is now wrong!
underTest.onAuthenticateButtonClicked()
assertThat(message?.text).isEqualTo(WRONG_PIN)
- assertThat(pinLengths).isEqualTo(0 to 0)
+ assertThat(entries).hasSize(0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -266,5 +290,11 @@ class PinBouncerViewModelTest : SysuiTestCase() {
private const val CONTAINER_NAME = "container1"
private const val ENTER_YOUR_PIN = "Enter your pin"
private const val WRONG_PIN = "Wrong pin"
+
+ val KEY_CODE =
+ Correspondence.transforming<EnteredKey, Int>(
+ { it?.input },
+ "has a eventId of",
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
index b2e37ccb045b..4ba67182c32c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
@@ -145,7 +145,7 @@ class ControlsProviderSelectorActivityTest : SysuiTestCase() {
assertThat(activityRule.activity.lastStartedActivity?.component?.className)
.isEqualTo(ControlsFavoritingActivity::class.java.name)
- assertThat(activityRule.activity.triedToFinish).isTrue()
+ assertThat(activityRule.activity.triedToFinish).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 8ee7d3e86265..c9470bb41ba5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -34,6 +34,7 @@ import com.android.systemui.R
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -185,6 +186,7 @@ class CustomizationProviderTest : SysuiTestCase() {
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
+ configurationRepository = FakeConfigurationRepository(),
),
registry = mock(),
lockPatternUtils = lockPatternUtils,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index 78a65a8473db..eb970221388b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -7,11 +7,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -60,13 +58,12 @@ class ResourceTrimmerTest : SysuiTestCase() {
keyguardRepository.setDozeAmount(0f)
keyguardRepository.setKeyguardGoingAway(false)
- val keyguardInteractor =
- KeyguardInteractor(
- keyguardRepository,
- FakeCommandQueue(),
- featureFlags,
- FakeKeyguardBouncerRepository()
+ val withDeps =
+ KeyguardInteractorFactory.create(
+ repository = keyguardRepository,
+ featureFlags = featureFlags,
)
+ val keyguardInteractor = withDeps.keyguardInteractor
resourceTrimmer =
ResourceTrimmer(
keyguardInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index e61620beeff3..3ea74e582229 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -47,6 +47,7 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus
@@ -159,17 +160,16 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
biometricSettingsRepository = FakeBiometricSettingsRepository()
deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
trustRepository = FakeTrustRepository()
- keyguardRepository = FakeKeyguardRepository()
- bouncerRepository = FakeKeyguardBouncerRepository()
featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
- fakeCommandQueue = FakeCommandQueue()
- keyguardInteractor =
- KeyguardInteractor(
- keyguardRepository,
- fakeCommandQueue,
- featureFlags,
- bouncerRepository
+ val withDeps =
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
)
+ keyguardInteractor = withDeps.keyguardInteractor
+ keyguardRepository = withDeps.repository
+ bouncerRepository = withDeps.bouncerRepository
+ fakeCommandQueue = withDeps.commandQueue
+
alternateBouncerInteractor =
AlternateBouncerInteractor(
bouncerRepository = bouncerRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 0d695aa231cc..a4f19b415061 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -22,6 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
@@ -50,6 +51,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
private lateinit var underTest: KeyguardInteractor
private lateinit var repository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+ private lateinit var configurationRepository: FakeConfigurationRepository
@Before
fun setUp() {
@@ -59,12 +61,14 @@ class KeyguardInteractorTest : SysuiTestCase() {
testScope = TestScope()
repository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
+ configurationRepository = FakeConfigurationRepository()
underTest =
KeyguardInteractor(
repository,
commandQueue,
featureFlags,
bouncerRepository,
+ configurationRepository,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index fb21847cd4d1..8f6bbc9d48d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -38,8 +38,6 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
@@ -48,7 +46,6 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
@@ -225,7 +222,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
@Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
@Mock private lateinit var expandable: Expandable
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
@@ -309,12 +305,10 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractor(
- repository = FakeKeyguardRepository(),
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ )
+ .keyguardInteractor,
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
@@ -368,11 +362,11 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
- underTest.onQuickAffordanceTriggered(
- configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
- expandable = expandable,
- slotId = "",
- )
+ underTest.onQuickAffordanceTriggered(
+ configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
+ expandable = expandable,
+ slotId = "",
+ )
if (startActivity) {
if (needsToUnlockFirst) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 895c1cd5a1a1..1c0b06cd85b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -40,7 +40,6 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
@@ -53,7 +52,6 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
@@ -84,7 +82,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
@@ -168,15 +165,14 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
set(Flags.FACE_AUTH_REFACTOR, true)
}
+ val withDeps =
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ repository = repository,
+ )
underTest =
KeyguardQuickAffordanceInteractor(
- keyguardInteractor =
- KeyguardInteractor(
- repository = repository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ keyguardInteractor = withDeps.keyguardInteractor,
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 603f199b468b..c53d4305f226 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -21,7 +21,6 @@ import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -39,7 +38,6 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -74,10 +72,10 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var shadeRepository: ShadeRepository
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var featureFlags: FakeFeatureFlags
// Used to verify transition requests for test output
@Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
@@ -103,11 +101,11 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
- val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
@@ -117,7 +115,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -126,7 +124,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -135,7 +133,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -144,7 +142,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -153,7 +151,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -162,7 +160,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromAlternateBouncerTransitionInteractor =
FromAlternateBouncerTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -171,7 +169,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromPrimaryBouncerTransitionInteractor =
FromPrimaryBouncerTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardSecurityModel = keyguardSecurityModel,
@@ -882,13 +880,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
WakeSleepReason.OTHER
)
- private fun createKeyguardInteractor(featureFlags: FeatureFlags): KeyguardInteractor {
- return KeyguardInteractor(
- keyguardRepository,
- commandQueue,
- featureFlags,
- bouncerRepository,
- )
+ private fun createKeyguardInteractor(): KeyguardInteractor {
+ return KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ repository = keyguardRepository,
+ bouncerRepository = bouncerRepository,
+ )
+ .keyguardInteractor
}
private suspend fun TestScope.runTransition(from: KeyguardState, to: KeyguardState) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
index d622f1c30816..65781c497944 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
@@ -95,7 +95,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.lockDevice()
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
underTest.dismissLockscreen()
@@ -108,7 +108,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.unlockDevice()
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
underTest.dismissLockscreen()
@@ -195,7 +195,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
assertThat(isUnlocked).isFalse()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 8a36dbc86e81..a493b1ddb93d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -39,12 +39,11 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -56,7 +55,6 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
@@ -95,7 +93,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
@@ -140,7 +137,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
),
),
)
- repository = FakeKeyguardRepository()
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
@@ -149,13 +145,10 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false)
}
- val keyguardInteractor =
- KeyguardInteractor(
- repository = repository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- )
+ val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ val keyguardInteractor = withDeps.keyguardInteractor
+ repository = withDeps.repository
+
whenever(userTracker.userHandle).thenReturn(mock())
whenever(userTracker.userId).thenReturn(10)
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
@@ -550,91 +543,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
}
@Test
- fun isIndicationAreaPadded() =
- testScope.runTest {
- repository.setKeyguardShowing(true)
- val value = collectLastValue(underTest.isIndicationAreaPadded)
-
- assertThat(value()).isFalse()
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_START,
- testConfig =
- TestConfig(
- isVisible = true,
- isClickable = true,
- icon = mock(),
- canShowWhileLocked = true,
- slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
- )
- )
- assertThat(value()).isTrue()
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_END,
- testConfig =
- TestConfig(
- isVisible = true,
- isClickable = true,
- icon = mock(),
- canShowWhileLocked = false,
- slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
- )
- )
- assertThat(value()).isTrue()
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_START,
- testConfig =
- TestConfig(
- isVisible = false,
- slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
- )
- )
- assertThat(value()).isTrue()
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_END,
- testConfig =
- TestConfig(
- isVisible = false,
- slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
- )
- )
- assertThat(value()).isFalse()
- }
-
- @Test
- fun indicationAreaTranslationX() =
- testScope.runTest {
- val value = collectLastValue(underTest.indicationAreaTranslationX)
-
- assertThat(value()).isEqualTo(0f)
- repository.setClockPosition(100, 100)
- assertThat(value()).isEqualTo(100f)
- repository.setClockPosition(200, 100)
- assertThat(value()).isEqualTo(200f)
- repository.setClockPosition(200, 200)
- assertThat(value()).isEqualTo(200f)
- repository.setClockPosition(300, 100)
- assertThat(value()).isEqualTo(300f)
- }
-
- @Test
- fun indicationAreaTranslationY() =
- testScope.runTest {
- val value =
- collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
-
- // Negative 0 - apparently there's a difference in floating point arithmetic - FML
- assertThat(value()).isEqualTo(-0f)
- val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
- assertThat(value()).isEqualTo(expected1)
- val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
- assertThat(value()).isEqualTo(expected2)
- val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
- assertThat(value()).isEqualTo(expected3)
- val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
- assertThat(value()).isEqualTo(expected4)
- }
-
- @Test
fun isClickable_trueWhenAlphaAtThreshold() =
testScope.runTest {
repository.setKeyguardShowing(true)
@@ -757,11 +665,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
)
}
- private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
- repository.setDozeAmount(dozeAmount)
- return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
- }
-
private suspend fun setUpQuickAffordanceModel(
position: KeyguardQuickAffordancePosition,
testConfig: TestConfig,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
new file mode 100644
index 000000000000..dff0f2909126
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
+
+ private lateinit var underTest: KeyguardIndicationAreaViewModel
+ private lateinit var repository: FakeKeyguardRepository
+
+ private val startButtonFlow =
+ MutableStateFlow<KeyguardQuickAffordanceViewModel>(
+ KeyguardQuickAffordanceViewModel(
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()
+ )
+ )
+ private val endButtonFlow =
+ MutableStateFlow<KeyguardQuickAffordanceViewModel>(
+ KeyguardQuickAffordanceViewModel(
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId()
+ )
+ )
+ private val alphaFlow = MutableStateFlow<Float>(1f)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
+ .thenReturn(RETURNED_BURN_IN_OFFSET)
+
+ val withDeps = KeyguardInteractorFactory.create()
+ val keyguardInteractor = withDeps.keyguardInteractor
+ repository = withDeps.repository
+
+ val bottomAreaViewModel: KeyguardBottomAreaViewModel = mock()
+ whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow)
+ whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow)
+ whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow)
+ underTest =
+ KeyguardIndicationAreaViewModel(
+ keyguardInteractor = keyguardInteractor,
+ bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
+ keyguardBottomAreaViewModel = bottomAreaViewModel,
+ burnInHelperWrapper = burnInHelperWrapper,
+ )
+ }
+
+ @Test
+ fun alpha() = runTest {
+ val value = collectLastValue(underTest.alpha)
+
+ assertThat(value()).isEqualTo(1f)
+ alphaFlow.value = 0.1f
+ assertThat(value()).isEqualTo(0.1f)
+ alphaFlow.value = 0.5f
+ assertThat(value()).isEqualTo(0.5f)
+ alphaFlow.value = 0.2f
+ assertThat(value()).isEqualTo(0.2f)
+ alphaFlow.value = 0f
+ assertThat(value()).isEqualTo(0f)
+ }
+
+ @Test
+ fun isIndicationAreaPadded() = runTest {
+ repository.setKeyguardShowing(true)
+ val value = collectLastValue(underTest.isIndicationAreaPadded)
+
+ assertThat(value()).isFalse()
+ startButtonFlow.value = startButtonFlow.value.copy(isVisible = true)
+ assertThat(value()).isTrue()
+ endButtonFlow.value = endButtonFlow.value.copy(isVisible = true)
+ assertThat(value()).isTrue()
+ startButtonFlow.value = startButtonFlow.value.copy(isVisible = false)
+ assertThat(value()).isTrue()
+ endButtonFlow.value = endButtonFlow.value.copy(isVisible = false)
+ assertThat(value()).isFalse()
+ }
+
+ @Test
+ fun indicationAreaTranslationX() = runTest {
+ val value = collectLastValue(underTest.indicationAreaTranslationX)
+
+ assertThat(value()).isEqualTo(0f)
+ repository.setClockPosition(100, 100)
+ assertThat(value()).isEqualTo(100f)
+ repository.setClockPosition(200, 100)
+ assertThat(value()).isEqualTo(200f)
+ repository.setClockPosition(200, 200)
+ assertThat(value()).isEqualTo(200f)
+ repository.setClockPosition(300, 100)
+ assertThat(value()).isEqualTo(300f)
+ }
+
+ @Test
+ fun indicationAreaTranslationY() = runTest {
+ val value = collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
+
+ // Negative 0 - apparently there's a difference in floating point arithmetic - FML
+ assertThat(value()).isEqualTo(-0f)
+ val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
+ assertThat(value()).isEqualTo(expected1)
+ val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
+ assertThat(value()).isEqualTo(expected2)
+ val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
+ assertThat(value()).isEqualTo(expected3)
+ val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
+ assertThat(value()).isEqualTo(expected4)
+ }
+
+ private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
+ repository.setDozeAmount(dozeAmount)
+ return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
+ }
+
+ companion object {
+ private const val DEFAULT_BURN_IN_OFFSET = 5
+ private const val RETURNED_BURN_IN_OFFSET = 3
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 8ba3f0f57eed..f0ea0077596a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -109,7 +109,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_swipeToUnlockedNotEnabled_bouncer() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -119,7 +119,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun onLockButtonClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
runCurrent()
@@ -132,7 +132,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
@@ -145,7 +145,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
runCurrent()
@@ -158,7 +158,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 105387d49bd4..05a16994e021 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -70,7 +70,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
@@ -83,7 +83,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index fe89a143e880..41351e53e0c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -89,12 +89,13 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.keyguard.KeyguardViewConfigurator;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -253,6 +254,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected UserManager mUserManager;
@Mock protected UiEventLogger mUiEventLogger;
@Mock protected LockIconViewController mLockIconViewController;
+ @Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator;
@Mock protected KeyguardMediaController mKeyguardMediaController;
@Mock protected NavigationModeController mNavigationModeController;
@Mock protected NavigationBarController mNavigationBarController;
@@ -342,8 +344,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mMainDispatcher = getMainDispatcher();
mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(
new FakeKeyguardRepository());
- mKeyguardInteractor = new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue,
- mFeatureFlags, new FakeKeyguardBouncerRepository());
+
+ mKeyguardInteractor = KeyguardInteractorFactory.create().getKeyguardInteractor();
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
mInteractionJankMonitor, mShadeExpansionStateManager);
@@ -611,6 +613,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mKeyuardLongPressViewModel,
mKeyguardInteractor,
mActivityStarter,
+ mKeyguardViewConfigurator,
mKeyguardFaceAuthInteractor);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 69d03d9b0e4c..f8e1a9d12657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -71,7 +71,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_deviceLocked_lockScreen() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen)
@@ -81,7 +81,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_deviceUnlocked_gone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
@@ -91,7 +91,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.unlockDevice()
runCurrent()
@@ -104,7 +104,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index fdfe028765ef..1724f27f63b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -44,7 +44,6 @@ import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeNotificationPresenter;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -122,7 +121,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mKeyguardStateController,
- mock(KeyguardIndicationController.class),
mCentralSurfaces,
mock(LockscreenShadeTransitionController.class),
mCommandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index d9ee08157c84..813597a8b576 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -28,12 +28,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.shade.ShadeFoldAnimator
import com.android.systemui.shade.ShadeViewController
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.unfold.util.FoldableDeviceStates
@@ -80,8 +78,6 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
@Mock lateinit var viewTreeObserver: ViewTreeObserver
- @Mock private lateinit var commandQueue: CommandQueue
-
@Mock lateinit var shadeFoldAnimator: ShadeFoldAnimator
@Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
@@ -111,15 +107,10 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
onActionStarted.run()
}
- keyguardRepository = FakeKeyguardRepository()
val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
- val keyguardInteractor =
- KeyguardInteractor(
- repository = keyguardRepository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- )
+ val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ val keyguardInteractor = withDeps.keyguardInteractor
+ keyguardRepository = withDeps.repository
// Needs to be run on the main thread
runBlocking(IMMEDIATE) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index ca83d49b19ca..3fbbeda8f74d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -39,12 +39,10 @@ import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -97,7 +95,6 @@ class UserInteractorTest : SysuiTestCase() {
@Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: UserInteractor
@@ -126,8 +123,9 @@ class UserInteractorTest : SysuiTestCase() {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
+ val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ keyguardRepository = reply.repository
userRepository = FakeUserRepository()
- keyguardRepository = FakeKeyguardRepository()
telephonyRepository = FakeTelephonyRepository()
val testDispatcher = StandardTestDispatcher()
testScope = TestScope(testDispatcher)
@@ -142,13 +140,7 @@ class UserInteractorTest : SysuiTestCase() {
applicationContext = context,
repository = userRepository,
activityStarter = activityStarter,
- keyguardInteractor =
- KeyguardInteractor(
- repository = keyguardRepository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ keyguardInteractor = reply.keyguardInteractor,
manager = manager,
headlessSystemUserMode = headlessSystemUserMode,
applicationScope = testScope.backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index fd8c6c76720b..9cb26e05887d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -32,11 +32,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -80,13 +77,11 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: StatusBarUserChipViewModel
private val userRepository = FakeUserRepository()
- private val keyguardRepository = FakeKeyguardRepository()
private lateinit var guestUserInteractor: GuestUserInteractor
private lateinit var refreshUsersScheduler: RefreshUsersScheduler
@@ -250,12 +245,8 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
repository = userRepository,
activityStarter = activityStarter,
keyguardInteractor =
- KeyguardInteractor(
- repository = keyguardRepository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ .keyguardInteractor,
featureFlags = featureFlags,
manager = manager,
headlessSystemUserMode = headlessSystemUserMode,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 91550844ceca..e3f9fac27815 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -30,11 +30,9 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -79,7 +77,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: UserSwitcherViewModel
@@ -112,7 +109,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
)
}
- keyguardRepository = FakeKeyguardRepository()
val refreshUsersScheduler =
RefreshUsersScheduler(
applicationScope = testScope.backgroundScope,
@@ -140,38 +136,35 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
+ val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ keyguardRepository = reply.repository
+
underTest =
UserSwitcherViewModel(
- userInteractor =
- UserInteractor(
- applicationContext = context,
- repository = userRepository,
- activityStarter = activityStarter,
- keyguardInteractor =
- KeyguardInteractor(
- repository = keyguardRepository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
- featureFlags = featureFlags,
- manager = manager,
- headlessSystemUserMode = headlessSystemUserMode,
- applicationScope = testScope.backgroundScope,
- telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
- broadcastDispatcher = fakeBroadcastDispatcher,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- backgroundDispatcher = testDispatcher,
- activityManager = activityManager,
- refreshUsersScheduler = refreshUsersScheduler,
- guestUserInteractor = guestUserInteractor,
- uiEventLogger = uiEventLogger,
- ),
- guestUserInteractor = guestUserInteractor,
- )
+ userInteractor =
+ UserInteractor(
+ applicationContext = context,
+ repository = userRepository,
+ activityStarter = activityStarter,
+ keyguardInteractor = reply.keyguardInteractor,
+ featureFlags = featureFlags,
+ manager = manager,
+ headlessSystemUserMode = headlessSystemUserMode,
+ applicationScope = testScope.backgroundScope,
+ telephonyInteractor =
+ TelephonyInteractor(
+ repository = FakeTelephonyRepository(),
+ ),
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ backgroundDispatcher = testDispatcher,
+ activityManager = activityManager,
+ refreshUsersScheduler = refreshUsersScheduler,
+ guestUserInteractor = guestUserInteractor,
+ uiEventLogger = uiEventLogger,
+ ),
+ guestUserInteractor = guestUserInteractor,
+ )
}
@Test
@@ -323,7 +316,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
setUsers(count = 2)
val isFinishRequested = mutableListOf<Boolean>()
val job =
- launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
+ launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
assertThat(isFinishRequested.last()).isFalse()
underTest.onCancelButtonClicked()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index b2a1668df7aa..b2a1668df7aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
new file mode 100644
index 000000000000..c7ca7ceb516e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 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.systemui.keyguard.domain.interactor
+
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+
+/**
+ * Simply put, I got tired of adding a constructor argument and then having to tweak dozens of
+ * files. This should alleviate some of the burden by providing defaults for testing.
+ */
+object KeyguardInteractorFactory {
+
+ @JvmOverloads
+ @JvmStatic
+ fun create(
+ featureFlags: FakeFeatureFlags = createFakeFeatureFlags(),
+ repository: FakeKeyguardRepository = FakeKeyguardRepository(),
+ commandQueue: FakeCommandQueue = FakeCommandQueue(),
+ bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
+ configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
+ ): WithDependencies {
+ return WithDependencies(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
+ bouncerRepository = bouncerRepository,
+ configurationRepository = configurationRepository,
+ KeyguardInteractor(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
+ bouncerRepository = bouncerRepository,
+ configurationRepository = configurationRepository,
+ )
+ )
+ }
+
+ /** Provide defaults, otherwise tests will throw an error */
+ fun createFakeFeatureFlags(): FakeFeatureFlags {
+ return FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
+ }
+
+ data class WithDependencies(
+ val repository: FakeKeyguardRepository,
+ val commandQueue: FakeCommandQueue,
+ val featureFlags: FakeFeatureFlags,
+ val bouncerRepository: FakeKeyguardBouncerRepository,
+ val configurationRepository: FakeConfigurationRepository,
+ val keyguardInteractor: KeyguardInteractor,
+ )
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 00dbb97191fb..6db8ea7e536e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2156,7 +2156,6 @@ public final class ActiveServices {
}
}
- final boolean enableFgsWhileInUseFix = mAm.mConstants.mEnableFgsWhileInUseFix;
final boolean fgsTypeChangingFromShortFgs = r.isForeground && isOldTypeShortFgs;
if (fgsTypeChangingFromShortFgs) {
@@ -2214,19 +2213,7 @@ public final class ActiveServices {
}
}
- boolean resetNeededForLogging = false;
-
- // Re-evaluate mAllowWhileInUsePermissionInFgs and mAllowStartForeground
- // (i.e. while-in-use and BFSL flags) if needed.
- //
- // Consider the below if-else section to be in the else of the above
- // `if (fgsTypeChangingFromShortFgs)`.
- // Using an else would increase the indent further, so we don't use it here
- // and instead just add !fgsTypeChangingFromShortFgs to all if's.
- //
- // The first if's are for the original while-in-use logic.
- if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix
- && r.mStartForegroundCount == 0) {
+ if (!fgsTypeChangingFromShortFgs && r.mStartForegroundCount == 0) {
/*
If the service was started with startService(), not
startForegroundService(), and if startForeground() isn't called within
@@ -2257,8 +2244,7 @@ public final class ActiveServices {
r.mLoggedInfoAllowStartForeground = false;
}
}
- } else if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix
- && r.mStartForegroundCount >= 1) {
+ } else if (!fgsTypeChangingFromShortFgs && r.mStartForegroundCount >= 1) {
// We get here if startForeground() is called multiple times
// on the same service after it's created, regardless of whether
// stopForeground() has been called or not.
@@ -2269,100 +2255,6 @@ public final class ActiveServices {
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */, false /* isStartService */);
- } else if (!fgsTypeChangingFromShortFgs && enableFgsWhileInUseFix) {
- // The new while-in-use logic.
- //
- // When startForeground() is called, we _always_ call
- // setFgsRestrictionLocked() to set the restrictions according to the
- // current state of the app.
- // (So if the app is now in TOP, for example, the service will now always
- // get while-in-use permissions.)
- //
- // Note, setFgsRestrictionLocked() will never disallow
- // mAllowWhileInUsePermissionInFgs nor mAllowStartForeground
- // (i.e. while-in-use and BFSL flags) once they're set to "allowed".
- //
- // HOWEVER, if these flags were set to "allowed" in Context.startService()
- // (as opposed to startForegroundService()), when the service wasn't yet
- // a foreground service, then we may not always
- // want to trust them -- for example, if the service has been running as a
- // BG service or a bound service for a long time when the app is not longer
- // in the foreground, then we shouldn't grant while-in-user nor BFSL.
- // So in that case, we need to reset it first.
-
- final long delayMs =
- (r.mLastUntrustedSetFgsRestrictionAllowedTime == 0) ? 0
- : (SystemClock.elapsedRealtime()
- - r.mLastUntrustedSetFgsRestrictionAllowedTime);
- final boolean resetNeeded =
- !r.isForeground
- && delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs;
- if (resetNeeded) {
- // We don't want to reset mDebugWhileInUseReasonInBindService here --
- // we'll instead reset it in the following code, using the simulated
- // legacy logic.
- resetFgsRestrictionLocked(r,
- /*resetDebugWhileInUseReasonInBindService=*/ false);
- }
-
- // Simulate the reset flow in the legacy logic to reset
- // mDebugWhileInUseReasonInBindService.
- // (Which is only used to compare to the old logic.)
- final long legacyDelayMs = SystemClock.elapsedRealtime() - r.createRealTime;
- if ((r.mStartForegroundCount == 0)
- && (legacyDelayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs)) {
- r.mDebugWhileInUseReasonInBindService = REASON_DENIED;
- }
-
- setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
- r.appInfo.uid, r.intent.getIntent(), r, r.userId,
- BackgroundStartPrivileges.NONE,
- false /* isBindService */, false /* isStartService */);
-
- final String temp = "startForegroundDelayMs:" + delayMs
- + "; started: " + r.startRequested
- + "; num_bindings: " + r.getConnections().size()
- + "; wasForeground: " + r.isForeground
- + "; resetNeeded:" + resetNeeded;
- if (r.mInfoAllowStartForeground != null) {
- r.mInfoAllowStartForeground += "; " + temp;
- } else {
- r.mInfoAllowStartForeground = temp;
- }
- r.mLoggedInfoAllowStartForeground = false;
-
- resetNeededForLogging = resetNeeded;
- }
-
- // If the service has any bindings and it's not yet a FGS
- // we compare the new and old while-in-use logics.
- // (If it's not the first startForeground() call, we already reset the
- // while-in-use and BFSL flags, so the logic change wouldn't matter.)
- //
- // Note, mDebugWhileInUseReasonInBindService does *not* fully simulate the
- // legacy logic, because we'll only set it in bindService(), but the actual
- // mAllowWhileInUsePermissionInFgsReason can change afterwards, in a subsequent
- // Service.startForeground(). This check will only provide "rough" check.
- // But if mDebugWhileInUseReasonInBindService is _not_ DENIED, and
- // mDebugWhileInUseReasonInStartForeground _is_ DENIED, then that means we'd
- // now detected a behavior change.
- // OTOH, if it's changing from non-DENIED to another non-DENIED, that may
- // not be a problem.
- if (enableFgsWhileInUseFix
- && !r.isForeground
- && (r.getConnections().size() > 0)
- && (r.mDebugWhileInUseReasonInBindService
- != r.mDebugWhileInUseReasonInStartForeground)) {
- logWhileInUseChangeWtf("FGS while-in-use changed (b/276963716): old="
- + reasonCodeToString(r.mDebugWhileInUseReasonInBindService)
- + " new="
- + reasonCodeToString(r.mDebugWhileInUseReasonInStartForeground)
- + " startForegroundCount=" + r.mStartForegroundCount
- + " started=" + r.startRequested
- + " num_bindings=" + r.getConnections().size()
- + " resetNeeded=" + resetNeededForLogging
- + " "
- + r.shortInstanceName);
}
// If the foreground service is not started from TOP process, do not allow it to
@@ -2471,10 +2363,6 @@ public final class ActiveServices {
}
r.isForeground = true;
- // Once the service becomes a foreground service,
- // the FGS restriction information always becomes "trustable".
- r.mLastUntrustedSetFgsRestrictionAllowedTime = 0;
-
// The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
// be deferred, make a copy of mAllowStartForeground and
// mAllowWhileInUsePermissionInFgs.
@@ -2620,13 +2508,6 @@ public final class ActiveServices {
}
}
- /**
- * It just does a wtf, but extracted to a method, so we can do a signature search on pitot.
- */
- private void logWhileInUseChangeWtf(String message) {
- Slog.wtf(TAG, message);
- }
-
private boolean withinFgsDeferRateLimit(ServiceRecord sr, final long now) {
// If we're still within the service's deferral period, then by definition
// deferral is not rate limited.
@@ -3839,25 +3720,9 @@ public final class ActiveServices {
return 0;
}
}
- if (!mAm.mConstants.mEnableFgsWhileInUseFix) {
- // Old while-in-use logic.
- setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
- BackgroundStartPrivileges.NONE, true /* isBindService */,
- false /* isStartService */);
- } else {
- // New logic will not call setFgsRestrictionLocked() here, but we still
- // keep track of the allow reason from the old logic here, so we can compare to
- // the new logic.
- // Once we're confident enough in the new logic, we should remove it.
- if (s.mDebugWhileInUseReasonInBindService == REASON_DENIED) {
- s.mDebugWhileInUseReasonInBindService =
- shouldAllowFgsWhileInUsePermissionLocked(
- callingPackage, callingPid, callingUid, s.app,
- BackgroundStartPrivileges.NONE,
- true /* isBindService */,
- false /* DO NOT enableFgsWhileInUseFix; use the old logic */);
- }
- }
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
+ BackgroundStartPrivileges.NONE, true /* isBindService */,
+ false /* isStartService */);
if (s.app != null) {
ProcessServiceRecord servicePsr = s.app.mServices;
@@ -7554,15 +7419,13 @@ public final class ActiveServices {
* @param r the service to start.
* @param isStartService True if it's called from Context.startService().
* False if it's called from Context.startForegroundService() or
- * Service.startService().
+ * Service.startForeground().
* @return true if allow, false otherwise.
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
boolean isStartService) {
- final long now = SystemClock.elapsedRealtime();
-
// Check DeviceConfig flag.
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
r.mAllowWhileInUsePermissionInFgs = true;
@@ -7580,60 +7443,30 @@ public final class ActiveServices {
isBindService);
// We store them to compare the old and new while-in-use logics to each other.
// (They're not used for any other purposes.)
- if (isBindService) {
- r.mDebugWhileInUseReasonInBindService = allowWhileInUse;
- } else {
- r.mDebugWhileInUseReasonInStartForeground = allowWhileInUse;
- }
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
- newlyAllowed |= r.mAllowWhileInUsePermissionInFgs;
}
if (r.mAllowStartForeground == REASON_DENIED) {
r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked(
allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
backgroundStartPrivileges, isBindService);
- newlyAllowed |= r.mAllowStartForeground != REASON_DENIED;
}
} else {
allowWhileInUse = REASON_UNKNOWN;
}
r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse;
-
- if (isStartService && !r.isForeground && newlyAllowed) {
- // If it's called by Context.startService() (not by startForegroundService()),
- // and we're setting "allowed", then we can't fully trust it yet -- we'll need to reset
- // the restrictions if startForeground() is called after the grace period.
- r.mLastUntrustedSetFgsRestrictionAllowedTime = now;
- }
}
/**
* Reset various while-in-use and BFSL related information.
*/
void resetFgsRestrictionLocked(ServiceRecord r) {
- resetFgsRestrictionLocked(r, /*resetDebugWhileInUseReasonInBindService=*/ true);
- }
-
- /**
- * Reset various while-in-use and BFSL related information.
- */
- void resetFgsRestrictionLocked(ServiceRecord r,
- boolean resetDebugWhileInUseReasonInBindService) {
r.mAllowWhileInUsePermissionInFgs = false;
r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
- r.mDebugWhileInUseReasonInStartForeground = REASON_DENIED;
-
- // In Service.startForeground(), we reset this field using a legacy logic,
- // so resetting this field is optional.
- if (resetDebugWhileInUseReasonInBindService) {
- r.mDebugWhileInUseReasonInBindService = REASON_DENIED;
- }
r.mAllowStartForeground = REASON_DENIED;
r.mInfoAllowStartForeground = null;
r.mInfoTempFgsAllowListReason = null;
r.mLoggedInfoAllowStartForeground = false;
- r.mLastUntrustedSetFgsRestrictionAllowedTime = 0;
r.updateAllowUiJobScheduling(r.mAllowWhileInUsePermissionInFgs);
}
@@ -7668,22 +7501,11 @@ public final class ActiveServices {
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
- return shouldAllowFgsWhileInUsePermissionLocked(callingPackage,
- callingPid, callingUid, targetProcess, backgroundStartPrivileges, isBindService,
- /* enableFgsWhileInUseFix =*/ mAm.mConstants.mEnableFgsWhileInUseFix);
- }
-
- private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
- int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
- BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
- boolean enableFgsWhileInUseFix) {
int ret = REASON_DENIED;
- // Define some local variables for better readability...
- final boolean useOldLogic = !enableFgsWhileInUseFix;
final boolean forStartForeground = !isBindService;
- if (useOldLogic || forStartForeground) {
+ if (forStartForeground) {
final int uidState = mAm.getUidStateLocked(callingUid);
if (ret == REASON_DENIED) {
// Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT,
@@ -7733,10 +7555,6 @@ public final class ActiveServices {
}
}
- if (enableFgsWhileInUseFix && ret == REASON_DENIED) {
- ret = shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid);
- }
-
if (ret == REASON_DENIED) {
// Allow FGS while-in-use if the WindowManager allows background activity start.
// This is mainly to get the 10 seconds grace period if any activity in the caller has
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 3b446338298d..44e198b53761 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -1058,13 +1058,6 @@ final class ActivityManagerConstants extends ContentObserver {
/** @see #KEY_USE_MODERN_TRIM */
public boolean USE_MODERN_TRIM = DEFAULT_USE_MODERN_TRIM;
- private static final String KEY_ENABLE_FGS_WHILE_IN_USE_FIX =
- "key_enable_fgs_while_in_use_fix";
-
- private static final boolean DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX = false;
-
- public volatile boolean mEnableFgsWhileInUseFix = DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX;
-
private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -1233,9 +1226,6 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION:
updateEnableWaitForFinishAttachApplication();
break;
- case KEY_ENABLE_FGS_WHILE_IN_USE_FIX:
- updateEnableFgsWhileInUseFix();
- break;
case KEY_MAX_PREVIOUS_TIME:
updateMaxPreviousTime();
break;
@@ -2005,12 +1995,6 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION);
}
- private void updateEnableFgsWhileInUseFix() {
- mEnableFgsWhileInUseFix = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_ENABLE_FGS_WHILE_IN_USE_FIX,
- DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX);
- }
private void updateUseTieredCachedAdj() {
USE_TIERED_CACHED_ADJ = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -2211,9 +2195,6 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print(" "); pw.print(KEY_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED);
pw.print("="); pw.println(mFlagSystemExemptPowerRestrictionsEnabled);
- pw.print(" "); pw.print(KEY_ENABLE_FGS_WHILE_IN_USE_FIX);
- pw.print("="); pw.println(mEnableFgsWhileInUseFix);
-
pw.print(" "); pw.print(KEY_SHORT_FGS_TIMEOUT_DURATION);
pw.print("="); pw.println(mShortFgsTimeoutDuration);
pw.print(" "); pw.print(KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 460ce4484a4b..1bd0675e95b6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4854,7 +4854,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
checkTime(startTime, "finishAttachApplicationInner: "
+ "after dispatching broadcasts");
- } catch (Exception e) {
+ } catch (BroadcastDeliveryFailedException e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
diff --git a/services/core/java/com/android/server/am/BroadcastDeliveryFailedException.java b/services/core/java/com/android/server/am/BroadcastDeliveryFailedException.java
new file mode 100644
index 000000000000..9c9281635cf7
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastDeliveryFailedException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 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.server.am;
+
+import android.util.AndroidException;
+
+/**
+ * Exception to represent that broadcast could not be delivered.
+ */
+public class BroadcastDeliveryFailedException extends AndroidException {
+ public BroadcastDeliveryFailedException(String name) {
+ super(name);
+ }
+
+ public BroadcastDeliveryFailedException(Exception cause) {
+ super(cause);
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 9b53af2e9b5c..0b5b1cb2902e 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -280,6 +280,25 @@ class BroadcastProcessQueue {
}
/**
+ * Re-enqueue the active broadcast so that it can be made active and delivered again. In order
+ * to keep its previous position same to avoid issues with reordering, insert it at the head
+ * of the queue.
+ *
+ * Callers are responsible for clearing the active broadcast by calling
+ * {@link #makeActiveIdle()} after re-enqueuing it.
+ */
+ public void reEnqueueActiveBroadcast() {
+ final BroadcastRecord record = getActive();
+ final int recordIndex = getActiveIndex();
+
+ final SomeArgs broadcastArgs = SomeArgs.obtain();
+ broadcastArgs.arg1 = record;
+ broadcastArgs.argi1 = recordIndex;
+ getQueueForBroadcast(record).addFirst(broadcastArgs);
+ onBroadcastEnqueued(record, recordIndex);
+ }
+
+ /**
* Searches from newest to oldest in the pending broadcast queues, and at the first matching
* pending broadcast it finds, replaces it in-place and returns -- does not attempt to handle
* "duplicate" broadcasts in the queue.
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8e76e5b5cf48..e38a2eefcd92 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -148,7 +148,8 @@ public abstract class BroadcastQueue {
* dispatching a pending broadcast
*/
@GuardedBy("mService")
- public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app);
+ public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app)
+ throws BroadcastDeliveryFailedException;
/**
* Signal from OS internals that the given process has timed out during
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index e389821595a0..4eedfe259633 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -438,7 +438,8 @@ public class BroadcastQueueImpl extends BroadcastQueue {
scheduleBroadcastsLocked();
}
- public boolean onApplicationAttachedLocked(ProcessRecord app) {
+ public boolean onApplicationAttachedLocked(ProcessRecord app)
+ throws BroadcastDeliveryFailedException {
updateUidReadyForBootCompletedBroadcastLocked(app.uid);
if (mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
@@ -460,7 +461,8 @@ public class BroadcastQueueImpl extends BroadcastQueue {
skipCurrentOrPendingReceiverLocked(app);
}
- public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
+ public boolean sendPendingBroadcastsLocked(ProcessRecord app)
+ throws BroadcastDeliveryFailedException {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast;
if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
@@ -483,7 +485,7 @@ public class BroadcastQueueImpl extends BroadcastQueue {
scheduleBroadcastsLocked();
// We need to reset the state if we failed to start the receiver.
br.state = BroadcastRecord.IDLE;
- throw new RuntimeException(e.getMessage());
+ throw new BroadcastDeliveryFailedException(e);
}
}
return didSomething;
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 22c0855ec54f..bb5fcbec7010 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -469,10 +469,15 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (DEBUG_BROADCAST) logv("Promoting " + queue
+ " from runnable to running; process is " + queue.app);
promoteToRunningLocked(queue);
- final boolean completed;
+ boolean completed;
if (processWarm) {
updateOomAdj |= queue.runningOomAdjusted;
- completed = scheduleReceiverWarmLocked(queue);
+ try {
+ completed = scheduleReceiverWarmLocked(queue);
+ } catch (BroadcastDeliveryFailedException e) {
+ reEnqueueActiveBroadcast(queue);
+ completed = true;
+ }
} else {
completed = scheduleReceiverColdLocked(queue);
}
@@ -513,7 +518,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
private void clearInvalidPendingColdStart() {
logw("Clearing invalid pending cold start: " + mRunningColdStart);
- onApplicationCleanupLocked(mRunningColdStart.app);
+ mRunningColdStart.reEnqueueActiveBroadcast();
+ demoteFromRunningLocked(mRunningColdStart);
+ clearRunningColdStart();
}
private void checkPendingColdStartValidity() {
@@ -535,8 +542,19 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
}
+ private void reEnqueueActiveBroadcast(@NonNull BroadcastProcessQueue queue) {
+ checkState(queue.isActive(), "isActive");
+
+ final BroadcastRecord record = queue.getActive();
+ final int index = queue.getActiveIndex();
+ setDeliveryState(queue, queue.app, record, index, record.receivers.get(index),
+ BroadcastRecord.DELIVERY_PENDING, "reEnqueueActiveBroadcast");
+ queue.reEnqueueActiveBroadcast();
+ }
+
@Override
- public boolean onApplicationAttachedLocked(@NonNull ProcessRecord app) {
+ public boolean onApplicationAttachedLocked(@NonNull ProcessRecord app)
+ throws BroadcastDeliveryFailedException {
// Process records can be recycled, so always start by looking up the
// relevant per-process queue
final BroadcastProcessQueue queue = getProcessQueue(app);
@@ -557,8 +575,14 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
queue.traceProcessEnd();
queue.traceProcessRunningBegin();
- if (scheduleReceiverWarmLocked(queue)) {
+ try {
+ if (scheduleReceiverWarmLocked(queue)) {
+ demoteFromRunningLocked(queue);
+ }
+ } catch (BroadcastDeliveryFailedException e) {
+ reEnqueueActiveBroadcast(queue);
demoteFromRunningLocked(queue);
+ throw e;
}
// We might be willing to kick off another cold start
@@ -588,14 +612,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
- // We've been waiting for this app to cold start, and it had
- // trouble; clear the slot and fail delivery below
- mRunningColdStart = null;
-
- queue.traceProcessEnd();
-
- // We might be willing to kick off another cold start
- enqueueUpdateRunningList();
+ clearRunningColdStart();
}
if (queue != null) {
@@ -618,6 +635,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
}
+ private void clearRunningColdStart() {
+ mRunningColdStart.traceProcessEnd();
+
+ // We've been waiting for this app to cold start, and it had
+ // trouble; clear the slot and fail delivery below
+ mRunningColdStart = null;
+
+ // We might be willing to kick off another cold start
+ enqueueUpdateRunningList();
+ }
+
@Override
public int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app) {
final BroadcastProcessQueue queue = getProcessQueue(app);
@@ -836,7 +864,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
*/
@CheckResult
@GuardedBy("mService")
- private boolean scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue) {
+ private boolean scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue)
+ throws BroadcastDeliveryFailedException {
checkState(queue.isActive(), "isActive");
final int cookie = traceBegin("scheduleReceiverWarmLocked");
@@ -918,7 +947,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
*/
@CheckResult
private boolean dispatchReceivers(@NonNull BroadcastProcessQueue queue,
- @NonNull BroadcastRecord r, int index) {
+ @NonNull BroadcastRecord r, int index) throws BroadcastDeliveryFailedException {
final ProcessRecord app = queue.app;
final Object receiver = r.receivers.get(index);
@@ -1005,6 +1034,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
logw(msg);
app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
+ // If we were trying to deliver a manifest broadcast, throw the error as we need
+ // to try redelivering the broadcast to this receiver.
+ if (receiver instanceof ResolveInfo) {
+ throw new BroadcastDeliveryFailedException(e);
+ }
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"remote app");
return false;
@@ -1092,7 +1126,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
boolean waitForServices) {
final BroadcastProcessQueue queue = getProcessQueue(app);
if ((queue == null) || !queue.isActive()) {
- logw("Ignoring finish; no active broadcast for " + queue);
+ logw("Ignoring finishReceiverLocked; no active broadcast for " + queue);
return false;
}
@@ -1129,7 +1163,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// We're on a roll; move onto the next broadcast for this process
queue.makeActiveNextPending();
- if (scheduleReceiverWarmLocked(queue)) {
+ try {
+ if (scheduleReceiverWarmLocked(queue)) {
+ demoteFromRunningLocked(queue);
+ return true;
+ }
+ } catch (BroadcastDeliveryFailedException e) {
+ reEnqueueActiveBroadcast(queue);
demoteFromRunningLocked(queue);
return true;
}
@@ -1166,7 +1206,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
private void finishReceiverActiveLocked(@NonNull BroadcastProcessQueue queue,
@DeliveryState int deliveryState, @NonNull String reason) {
if (!queue.isActive()) {
- logw("Ignoring finish; no active broadcast for " + queue);
+ logw("Ignoring finishReceiverActiveLocked; no active broadcast for " + queue);
return;
}
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 564b1fe8de34..863dd63a2922 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -699,6 +699,9 @@ final class BroadcastRecord extends Binder {
break;
}
switch (newDeliveryState) {
+ case DELIVERY_PENDING:
+ scheduledTime[index] = 0;
+ break;
case DELIVERY_SCHEDULED:
scheduledTime[index] = SystemClock.uptimeMillis();
break;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 1f39d1b5d7f8..dccbb0accadd 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -177,12 +177,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
@PowerExemptionManager.ReasonCode
int mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
- // Integer version of mAllowWhileInUsePermissionInFgs that we keep track to compare
- // the old and new logics.
- // TODO: Remove them once we're confident in the new logic.
- int mDebugWhileInUseReasonInStartForeground = REASON_DENIED;
- int mDebugWhileInUseReasonInBindService = REASON_DENIED;
-
// A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
boolean mAllowWhileInUsePermissionInFgsAtEntering;
/** Allow scheduling user-initiated jobs from the background. */
@@ -224,13 +218,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
// is called.
int mStartForegroundCount;
- // Last time mAllowWhileInUsePermissionInFgs or mAllowStartForeground was set to "allowed"
- // from "disallowed" when the service was _not_ already a foreground service.
- // this means they're set in startService(). (not startForegroundService)
- // In startForeground(), if this timestamp is too old, we can't trust these flags, so
- // we need to reset them.
- long mLastUntrustedSetFgsRestrictionAllowedTime;
-
// This is a service record of a FGS delegate (not a service record of a real service)
boolean mIsFgsDelegate;
@Nullable ForegroundServiceDelegation mFgsDelegation;
@@ -624,12 +611,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
pw.println(PowerExemptionManager.reasonCodeToString(mAllowWhileInUsePermissionInFgsReason));
- pw.print(prefix); pw.print("debugWhileInUseReasonInStartForeground=");
- pw.println(PowerExemptionManager.reasonCodeToString(
- mDebugWhileInUseReasonInStartForeground));
- pw.print(prefix); pw.print("debugWhileInUseReasonInBindService=");
- pw.println(PowerExemptionManager.reasonCodeToString(mDebugWhileInUseReasonInBindService));
-
pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
pw.print(prefix); pw.print("recentCallingPackage=");
pw.println(mRecentCallingPackage);
@@ -642,10 +623,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
pw.print(prefix); pw.print("infoAllowStartForeground=");
pw.println(mInfoAllowStartForeground);
- pw.print(prefix); pw.print("lastUntrustedSetFgsRestrictionAllowedTime=");
- TimeUtils.formatDuration(mLastUntrustedSetFgsRestrictionAllowedTime, now, pw);
- pw.println();
-
if (delayed) {
pw.print(prefix); pw.print("delayed="); pw.println(delayed);
}
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index de42370e6d84..651828b6b9e2 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -40,6 +40,7 @@ public class BrightnessSetting {
private final LogicalDisplay mLogicalDisplay;
+ private int mUserSerial;
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -56,13 +57,15 @@ public class BrightnessSetting {
@GuardedBy("mSyncRoot")
private float mBrightness;
- BrightnessSetting(@NonNull PersistentDataStore persistentDataStore,
+ BrightnessSetting(int userSerial,
+ @NonNull PersistentDataStore persistentDataStore,
@NonNull LogicalDisplay logicalDisplay,
DisplayManagerService.SyncRoot syncRoot) {
mPersistentDataStore = persistentDataStore;
mLogicalDisplay = logicalDisplay;
+ mUserSerial = userSerial;
mBrightness = mPersistentDataStore.getBrightness(
- mLogicalDisplay.getPrimaryDisplayDeviceLocked());
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked(), userSerial);
mSyncRoot = syncRoot;
}
@@ -96,8 +99,13 @@ public class BrightnessSetting {
mListeners.remove(l);
}
+ /** Sets the user serial for the brightness setting */
+ public void setUserSerial(int userSerial) {
+ mUserSerial = userSerial;
+ }
+
/**
- * Sets the brigtness and broadcasts the change to the listeners.
+ * Sets the brightness and broadcasts the change to the listeners.
* @param brightness The value to which the brightness is to be set.
*/
public void setBrightness(float brightness) {
@@ -112,7 +120,8 @@ public class BrightnessSetting {
// changed.
if (brightness != mBrightness) {
mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(),
- brightness);
+ brightness, mUserSerial
+ );
}
mBrightness = brightness;
int toSend = Float.floatToIntBits(mBrightness);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index ca482dc41ce5..7a797dd2250c 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -889,6 +889,13 @@ public class DisplayDeviceConfig {
}
/**
+ * @return true if there is sdrHdrRatioMap, false otherwise.
+ */
+ public boolean hasSdrToHdrRatioSpline() {
+ return mSdrToHdrRatioSpline != null;
+ }
+
+ /**
* Calculate the HDR brightness for the specified SDR brightenss, restricted by the
* maxDesiredHdrSdrRatio (the ratio between the HDR luminance and SDR luminance)
*
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 38445295b0f1..be65c531b913 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -652,6 +652,12 @@ public final class DisplayManagerService extends SystemService {
logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(),
userSerial);
dpc.setBrightnessConfiguration(config, /* shouldResetShortTermModel= */ true);
+ // change the brightness value according to the selected user.
+ final DisplayDevice device = logicalDisplay.getPrimaryDisplayDeviceLocked();
+ if (device != null) {
+ dpc.setBrightness(
+ mPersistentDataStore.getBrightness(device, userSerial), userSerial);
+ }
}
dpc.onSwitchUser(newUserId);
});
@@ -3134,8 +3140,9 @@ public final class DisplayManagerService extends SystemService {
mBrightnessTracker = new BrightnessTracker(mContext, null);
}
- final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
- display, mSyncRoot);
+ final int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId());
+ final BrightnessSetting brightnessSetting = new BrightnessSetting(userSerial,
+ mPersistentDataStore, display, mSyncRoot);
final DisplayPowerControllerInterface displayPowerController;
// If display is internal and has a HighBrightnessModeMetadata mapping, use that.
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2b5523ae52b0..7d8bde9feabf 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2664,9 +2664,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
@Override
- public void setBrightness(float brightnessValue) {
+ public void setBrightness(float brightnessValue, int userSerial) {
// Update the setting, which will eventually call back into DPC to have us actually update
// the display with the new value.
+ mBrightnessSetting.setUserSerial(userSerial);
mBrightnessSetting.setBrightness(brightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
float nits = convertToNits(brightnessValue);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index cc9f5394df9b..040ceccbc7c9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -2172,8 +2172,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
@Override
- public void setBrightness(float brightnessValue) {
- mDisplayBrightnessController.setBrightness(brightnessValue);
+ public void setBrightness(float brightnessValue, int userSerial) {
+ mDisplayBrightnessController.setBrightness(brightnessValue, userSerial);
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 73edb970ff95..5fbbcbd2959f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -29,7 +29,7 @@ import java.io.PrintWriter;
* An interface to manage the display's power state and brightness
*/
public interface DisplayPowerControllerInterface {
-
+ int DEFAULT_USER_SERIAL = -1;
/**
* Notified when the display is changed.
*
@@ -98,7 +98,17 @@ public interface DisplayPowerControllerInterface {
* Set the screen brightness of the associated display
* @param brightness The value to which the brightness is to be set
*/
- void setBrightness(float brightness);
+ default void setBrightness(float brightness) {
+ setBrightness(brightness, DEFAULT_USER_SERIAL);
+ }
+
+ /**
+ * Set the screen brightness of the associated display
+ * @param brightness The value to which the brightness is to be set
+ * @param userSerial The user for which the brightness value is to be set. Use userSerial = -1,
+ * if brightness needs to be updated for the current user.
+ */
+ void setBrightness(float brightness, int userSerial);
/**
* Checks if the proximity sensor is available
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 79984c9b5355..c7c0fab6140d 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -888,7 +888,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
BrightnessSynchronizer.brightnessFloatToInt(
sdrBrightnessState));
- handleHdrSdrNitsChanged(nits, sdrNits);
+ if (getDisplayDeviceConfig().hasSdrToHdrRatioSpline()) {
+ handleHdrSdrNitsChanged(nits, sdrNits);
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 6d6ed726161b..2d7792d01c53 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -133,6 +133,7 @@ final class PersistentDataStore {
private static final String TAG_BRIGHTNESS_NITS_FOR_DEFAULT_DISPLAY =
"brightness-nits-for-default-display";
+ public static final int DEFAULT_USER_ID = -1;
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -294,7 +295,7 @@ final class PersistentDataStore {
return false;
}
- public float getBrightness(DisplayDevice device) {
+ public float getBrightness(DisplayDevice device, int userSerial) {
if (device == null || !device.hasStableUniqueId()) {
return Float.NaN;
}
@@ -302,10 +303,10 @@ final class PersistentDataStore {
if (state == null) {
return Float.NaN;
}
- return state.getBrightness();
+ return state.getBrightness(userSerial);
}
- public boolean setBrightness(DisplayDevice displayDevice, float brightness) {
+ public boolean setBrightness(DisplayDevice displayDevice, float brightness, int userSerial) {
if (displayDevice == null || !displayDevice.hasStableUniqueId()) {
return false;
}
@@ -314,7 +315,7 @@ final class PersistentDataStore {
return false;
}
final DisplayState state = getDisplayState(displayDeviceUniqueId, true);
- if (state.setBrightness(brightness)) {
+ if (state.setBrightness(brightness, userSerial)) {
setDirty();
return true;
}
@@ -611,6 +612,7 @@ final class PersistentDataStore {
state.saveToXml(serializer);
serializer.endTag(null, TAG_DISPLAY);
}
+
serializer.endTag(null, TAG_DISPLAY_STATES);
serializer.startTag(null, TAG_STABLE_DEVICE_VALUES);
mStableDeviceValues.saveToXml(serializer);
@@ -649,7 +651,8 @@ final class PersistentDataStore {
private static final class DisplayState {
private int mColorMode;
- private float mBrightness = Float.NaN;
+
+ private SparseArray<Float> mPerUserBrightness = new SparseArray<>();
private int mWidth;
private int mHeight;
private float mRefreshRate;
@@ -670,16 +673,25 @@ final class PersistentDataStore {
return mColorMode;
}
- public boolean setBrightness(float brightness) {
- if (brightness == mBrightness) {
+ public boolean setBrightness(float brightness, int userSerial) {
+ // Remove the default user brightness, before setting a new user-specific value.
+ // This is a one-time operation, required to restructure the config after user-specific
+ // brightness was introduced.
+ mPerUserBrightness.remove(DEFAULT_USER_ID);
+
+ if (getBrightness(userSerial) == brightness) {
return false;
}
- mBrightness = brightness;
+ mPerUserBrightness.set(userSerial, brightness);
return true;
}
- public float getBrightness() {
- return mBrightness;
+ public float getBrightness(int userSerial) {
+ float brightness = mPerUserBrightness.get(userSerial, Float.NaN);
+ if (Float.isNaN(brightness)) {
+ brightness = mPerUserBrightness.get(DEFAULT_USER_ID, Float.NaN);
+ }
+ return brightness;
}
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
@@ -729,12 +741,7 @@ final class PersistentDataStore {
mColorMode = Integer.parseInt(value);
break;
case TAG_BRIGHTNESS_VALUE:
- String brightness = parser.nextText();
- try {
- mBrightness = Float.parseFloat(brightness);
- } catch (NumberFormatException e) {
- mBrightness = Float.NaN;
- }
+ loadBrightnessFromXml(parser);
break;
case TAG_BRIGHTNESS_CONFIGURATIONS:
mDisplayBrightnessConfigurations.loadFromXml(parser);
@@ -760,11 +767,12 @@ final class PersistentDataStore {
serializer.text(Integer.toString(mColorMode));
serializer.endTag(null, TAG_COLOR_MODE);
- serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
- if (!Float.isNaN(mBrightness)) {
- serializer.text(Float.toString(mBrightness));
+ for (int i = 0; i < mPerUserBrightness.size(); i++) {
+ serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
+ serializer.attributeInt(null, ATTR_USER_SERIAL, mPerUserBrightness.keyAt(i));
+ serializer.text(Float.toString(mPerUserBrightness.valueAt(i)));
+ serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
}
- serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
mDisplayBrightnessConfigurations.saveToXml(serializer);
@@ -785,12 +793,33 @@ final class PersistentDataStore {
public void dump(final PrintWriter pw, final String prefix) {
pw.println(prefix + "ColorMode=" + mColorMode);
- pw.println(prefix + "BrightnessValue=" + mBrightness);
+ pw.println(prefix + "BrightnessValues: ");
+ for (int i = 0; i < mPerUserBrightness.size(); i++) {
+ pw.println("User: " + mPerUserBrightness.keyAt(i)
+ + " Value: " + mPerUserBrightness.valueAt(i));
+ }
pw.println(prefix + "DisplayBrightnessConfigurations: ");
mDisplayBrightnessConfigurations.dump(pw, prefix);
pw.println(prefix + "Resolution=" + mWidth + " " + mHeight);
pw.println(prefix + "RefreshRate=" + mRefreshRate);
}
+
+ private void loadBrightnessFromXml(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int userSerial;
+ try {
+ userSerial = parser.getAttributeInt(null, ATTR_USER_SERIAL);
+ } catch (NumberFormatException | XmlPullParserException e) {
+ userSerial = DEFAULT_USER_ID;
+ Slog.e(TAG, "Failed to read user serial", e);
+ }
+ String brightness = parser.nextText();
+ try {
+ mPerUserBrightness.set(userSerial, Float.parseFloat(brightness));
+ } catch (NumberFormatException nfe) {
+ Slog.e(TAG, "Failed to read brightness", nfe);
+ }
+ }
}
private static final class StableDeviceValues {
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 7574de841440..2f52b708dfb5 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -38,6 +38,8 @@ import java.io.PrintWriter;
* display. Applies the chosen brightness.
*/
public final class DisplayBrightnessController {
+ private static final int DEFAULT_USER_SERIAL = -1;
+
// The ID of the display tied to this DisplayBrightnessController
private final int mDisplayId;
@@ -274,8 +276,16 @@ public final class DisplayBrightnessController {
* Notifies the brightnessSetting to persist the supplied brightness value.
*/
public void setBrightness(float brightnessValue) {
+ setBrightness(brightnessValue, DEFAULT_USER_SERIAL);
+ }
+
+ /**
+ * Notifies the brightnessSetting to persist the supplied brightness value for a user.
+ */
+ public void setBrightness(float brightnessValue, int userSerial) {
// Update the setting, which will eventually call back into DPC to have us actually
// update the display with the new value.
+ mBrightnessSetting.setUserSerial(userSerial);
mBrightnessSetting.setBrightness(brightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
float nits = convertToNits(brightnessValue);
diff --git a/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java b/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
index 873d5fc92601..4bc20a555dec 100644
--- a/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
+++ b/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java
@@ -42,6 +42,12 @@ public class HdmiEarcLocalDeviceTx extends HdmiEarcLocalDevice {
// How long to wait for the audio system to report its capabilities after eARC was connected
static final long REPORT_CAPS_MAX_DELAY_MS = 2_000;
+ // Array containing the names of the eARC states. The integer value of the eARC state
+ // corresponds to the index in the array.
+ private static final String earcStatusNames[] = {"HDMI_EARC_STATUS_IDLE",
+ "HDMI_EARC_STATUS_EARC_PENDING", "HDMI_EARC_STATUS_ARC_PENDING",
+ "HDMI_EARC_STATUS_EARC_CONNECTED"};
+
// eARC Capability Data Structure parameters
private static final int EARC_CAPS_PAYLOAD_LENGTH = 0x02;
private static final int EARC_CAPS_DATA_START = 0x03;
@@ -75,11 +81,17 @@ public class HdmiEarcLocalDeviceTx extends HdmiEarcLocalDevice {
mReportCapsRunnable = new ReportCapsRunnable();
}
+ private String earcStatusToString(int status) {
+ return earcStatusNames[status];
+ }
+
protected void handleEarcStateChange(@Constants.EarcStatus int status) {
int oldEarcStatus;
+
synchronized (mLock) {
- HdmiLogger.debug("eARC state change [old:%b new %b]", mEarcStatus,
- status);
+ HdmiLogger.debug("eARC state change [old: %s(%d) new: %s(%d)]",
+ earcStatusToString(mEarcStatus), mEarcStatus,
+ earcStatusToString(status), status);
oldEarcStatus = mEarcStatus;
mEarcStatus = status;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e56eba659d37..c496857bb63b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6746,6 +6746,8 @@ public class NotificationManagerService extends SystemService {
return false;
}
+ mUsageStats.registerEnqueuedByAppAndAccepted(pkg);
+
if (info != null) {
// Cache the shortcut synchronously after the associated notification is posted in case
// the app unpublishes this shortcut immediately after posting the notification. If the
@@ -7184,11 +7186,12 @@ public class NotificationManagerService extends SystemService {
+ " cannot create notifications");
}
- // rate limit updates that aren't completed progress notifications
- if (mNotificationsByKey.get(r.getSbn().getKey()) != null
- && !r.getNotification().hasCompletedProgress()
- && !isAutogroup) {
-
+ // Rate limit updates that aren't completed progress notifications
+ // Search for the original one in the posted and not-yet-posted (enqueued) lists.
+ boolean isUpdate = mNotificationsByKey.get(r.getSbn().getKey()) != null
+ || findNotificationByListLocked(mEnqueuedNotifications, r.getSbn().getKey())
+ != null;
+ if (isUpdate && !r.getNotification().hasCompletedProgress() && !isAutogroup) {
final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
if (appEnqueueRate > mMaxPackageEnqueueRate) {
mUsageStats.registerOverRateQuota(pkg);
@@ -7813,15 +7816,8 @@ public class NotificationManagerService extends SystemService {
boolean posted = false;
synchronized (mNotificationLock) {
try {
- NotificationRecord r = null;
- int N = mEnqueuedNotifications.size();
- for (int i = 0; i < N; i++) {
- final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
- if (Objects.equals(key, enqueued.getKey())) {
- r = enqueued;
- break;
- }
- }
+ NotificationRecord r = findNotificationByListLocked(mEnqueuedNotifications,
+ key);
if (r == null) {
Slog.i(TAG, "Cannot find enqueued record for key: " + key);
return false;
@@ -9486,7 +9482,7 @@ public class NotificationManagerService extends SystemService {
* Determine whether the userId applies to the notification in question, either because
* they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
*/
- private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
+ private static boolean notificationMatchesUserId(NotificationRecord r, int userId) {
return
// looking for USER_ALL notifications? match everything
userId == UserHandle.USER_ALL
@@ -9845,9 +9841,9 @@ public class NotificationManagerService extends SystemService {
return null;
}
- @GuardedBy("mNotificationLock")
- private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
- String pkg, String tag, int id, int userId) {
+ @Nullable
+ private static NotificationRecord findNotificationByListLocked(
+ ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) {
final int len = list.size();
for (int i = 0; i < len; i++) {
NotificationRecord r = list.get(i);
@@ -9860,8 +9856,7 @@ public class NotificationManagerService extends SystemService {
return null;
}
- @GuardedBy("mNotificationLock")
- private List<NotificationRecord> findNotificationsByListLocked(
+ private static List<NotificationRecord> findNotificationsByListLocked(
ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) {
List<NotificationRecord> matching = new ArrayList<>();
final int len = list.size();
@@ -9876,9 +9871,9 @@ public class NotificationManagerService extends SystemService {
return matching;
}
- @GuardedBy("mNotificationLock")
- private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
- String key) {
+ @Nullable
+ private static NotificationRecord findNotificationByListLocked(
+ ArrayList<NotificationRecord> list, String key) {
final int N = list.size();
for (int i = 0; i < N; i++) {
if (key.equals(list.get(i).getKey())) {
@@ -9888,29 +9883,6 @@ public class NotificationManagerService extends SystemService {
return null;
}
- /**
- * There may be multiple records that match your criteria. For instance if there have been
- * multiple notifications posted which are enqueued for the same pkg, tag, id, userId. This
- * method will find all of them in the given list
- * @return
- */
- @GuardedBy("mNotificationLock")
- private List<NotificationRecord> findEnqueuedNotificationsForCriteria(
- String pkg, String tag, int id, int userId) {
- final ArrayList<NotificationRecord> records = new ArrayList<>();
- final int n = mEnqueuedNotifications.size();
- for (int i = 0; i < n; i++) {
- NotificationRecord r = mEnqueuedNotifications.get(i);
- if (notificationMatchesUserId(r, userId)
- && r.getSbn().getId() == id
- && TextUtils.equals(r.getSbn().getTag(), tag)
- && r.getSbn().getPackageName().equals(pkg)) {
- records.add(r);
- }
- }
- return records;
- }
-
@GuardedBy("mNotificationLock")
int indexOfNotificationLocked(String key) {
final int N = mNotificationList.size();
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index ffe33a832ce4..e960f4ba11fd 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -16,23 +16,16 @@
package com.android.server.notification;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-
import android.app.Notification;
-import android.content.ContentValues;
import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteFullException;
-import android.database.sqlite.SQLiteOpenHelper;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -42,8 +35,6 @@ import org.json.JSONObject;
import java.io.PrintWriter;
import java.util.ArrayDeque;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -63,7 +54,6 @@ public class NotificationUsageStats {
private static final String TAG = "NotificationUsageStats";
private static final boolean ENABLE_AGGREGATED_IN_MEMORY_STATS = true;
- private static final boolean ENABLE_SQLITE_LOG = true;
private static final AggregatedStats[] EMPTY_AGGREGATED_STATS = new AggregatedStats[0];
private static final String DEVICE_GLOBAL_STATS = "__global"; // packages start with letters
private static final int MSG_EMIT = 1;
@@ -73,12 +63,15 @@ public class NotificationUsageStats {
public static final int FOUR_HOURS = 1000 * 60 * 60 * 4;
private static final long EMIT_PERIOD = DEBUG ? TEN_SECONDS : FOUR_HOURS;
- // Guarded by synchronized(this).
+ @GuardedBy("this")
private final Map<String, AggregatedStats> mStats = new HashMap<>();
+ @GuardedBy("this")
private final ArrayDeque<AggregatedStats[]> mStatsArrays = new ArrayDeque<>();
+ @GuardedBy("this")
private ArraySet<String> mStatExpiredkeys = new ArraySet<>();
private final Context mContext;
private final Handler mHandler;
+ @GuardedBy("this")
private long mLastEmitTime;
public NotificationUsageStats(Context context) {
@@ -105,11 +98,7 @@ public class NotificationUsageStats {
*/
public synchronized float getAppEnqueueRate(String packageName) {
AggregatedStats stats = getOrCreateAggregatedStatsLocked(packageName);
- if (stats != null) {
- return stats.getEnqueueRate(SystemClock.elapsedRealtime());
- } else {
- return 0f;
- }
+ return stats.getEnqueueRate(SystemClock.elapsedRealtime());
}
/**
@@ -117,11 +106,7 @@ public class NotificationUsageStats {
*/
public synchronized boolean isAlertRateLimited(String packageName) {
AggregatedStats stats = getOrCreateAggregatedStatsLocked(packageName);
- if (stats != null) {
- return stats.isAlertRateLimited();
- } else {
- return false;
- }
+ return stats.isAlertRateLimited();
}
/**
@@ -136,16 +121,32 @@ public class NotificationUsageStats {
}
/**
+ * Called when a notification that was enqueued by an app is effectively enqueued to be
+ * posted. This is after rate checking, to update the rate.
+ *
+ * <p>Note that if we updated the arrival estimate <em>before</em> checking it, then an app
+ * enqueueing at slightly above the acceptable rate would never get their notifications
+ * accepted; updating afterwards allows the rate to dip below the threshold and thus lets
+ * through some of them.
+ */
+ public synchronized void registerEnqueuedByAppAndAccepted(String packageName) {
+ final long now = SystemClock.elapsedRealtime();
+ AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(packageName);
+ for (AggregatedStats stats : aggregatedStatsArray) {
+ stats.updateInterarrivalEstimate(now);
+ }
+ releaseAggregatedStatsLocked(aggregatedStatsArray);
+ }
+
+ /**
* Called when a notification has been posted.
*/
public synchronized void registerPostedByApp(NotificationRecord notification) {
- final long now = SystemClock.elapsedRealtime();
- notification.stats.posttimeElapsedMs = now;
+ notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime();
AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
for (AggregatedStats stats : aggregatedStatsArray) {
stats.numPostedByApp++;
- stats.updateInterarrivalEstimate(now);
stats.countApiUse(notification);
stats.numUndecoratedRemoteViews += (notification.hasUndecoratedRemoteView() ? 1 : 0);
}
@@ -161,7 +162,6 @@ public class NotificationUsageStats {
AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
for (AggregatedStats stats : aggregatedStatsArray) {
stats.numUpdatedByApp++;
- stats.updateInterarrivalEstimate(SystemClock.elapsedRealtime());
stats.countApiUse(notification);
}
releaseAggregatedStatsLocked(aggregatedStatsArray);
@@ -257,12 +257,12 @@ public class NotificationUsageStats {
}
}
- // Locked by this.
+ @GuardedBy("this")
private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) {
return getAggregatedStatsLocked(record.getSbn().getPackageName());
}
- // Locked by this.
+ @GuardedBy("this")
private AggregatedStats[] getAggregatedStatsLocked(String packageName) {
if (!ENABLE_AGGREGATED_IN_MEMORY_STATS) {
return EMPTY_AGGREGATED_STATS;
@@ -277,7 +277,7 @@ public class NotificationUsageStats {
return array;
}
- // Locked by this.
+ @GuardedBy("this")
private void releaseAggregatedStatsLocked(AggregatedStats[] array) {
for(int i = 0; i < array.length; i++) {
array[i] = null;
@@ -285,7 +285,7 @@ public class NotificationUsageStats {
mStatsArrays.offer(array);
}
- // Locked by this.
+ @GuardedBy("this")
private AggregatedStats getOrCreateAggregatedStatsLocked(String key) {
AggregatedStats result = mStats.get(key);
if (result == null) {
diff --git a/services/core/java/com/android/server/notification/RateEstimator.java b/services/core/java/com/android/server/notification/RateEstimator.java
index a2f93dce2bca..19768a286fd9 100644
--- a/services/core/java/com/android/server/notification/RateEstimator.java
+++ b/services/core/java/com/android/server/notification/RateEstimator.java
@@ -22,9 +22,10 @@ package com.android.server.notification;
*
* {@hide}
*/
-public class RateEstimator {
- private static final double RATE_ALPHA = 0.8;
+class RateEstimator {
+ private static final double RATE_ALPHA = 0.5;
private static final double MINIMUM_DT = 0.0005;
+
private Long mLastEventTime;
private double mInterarrivalTime;
@@ -34,18 +35,12 @@ public class RateEstimator {
}
/** Update the estimate to account for an event that just happened. */
- public float update(long now) {
- float rate;
- if (mLastEventTime == null) {
- // No last event time, rate is zero.
- rate = 0f;
- } else {
+ public void update(long now) {
+ if (mLastEventTime != null) {
// Calculate the new inter-arrival time based on last event time.
mInterarrivalTime = getInterarrivalEstimate(now);
- rate = (float) (1.0 / mInterarrivalTime);
}
mLastEventTime = now;
- return rate;
}
/** @return the estimated rate if there were a new event right now. */
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b1a289f046bd..2806a1176e1c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6053,12 +6053,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- @WindowManagerFuncs.LidState
- public int getLidState() {
- return mDefaultDisplayPolicy.getLidState();
- }
-
- @Override
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 3da78123016b..887f9461bdce 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -218,14 +218,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* between it and the policy.
*/
public interface WindowManagerFuncs {
- @IntDef(prefix = { "LID_" }, value = {
- LID_ABSENT,
- LID_CLOSED,
- LID_OPEN,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface LidState{}
-
public static final int LID_ABSENT = -1;
public static final int LID_CLOSED = 0;
public static final int LID_OPEN = 1;
@@ -239,9 +231,8 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
public static final int CAMERA_LENS_COVERED = 1;
/**
- * Returns a {@link LidState} that describes the current state of the lid switch.
+ * Returns a code that describes the current state of the lid switch.
*/
- @LidState
public int getLidState();
/**
@@ -291,7 +282,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Convert the lid state to a human readable format.
*/
- static String lidStateToString(@LidState int lid) {
+ static String lidStateToString(int lid) {
switch (lid) {
case LID_ABSENT:
return "LID_ABSENT";
@@ -1250,11 +1241,4 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* @return {@code true} if the key will be handled globally.
*/
boolean isGlobalKey(int keyCode);
-
- /**
- * Returns a {@link WindowManagerFuncs.LidState} that describes the current state of
- * the lid switch.
- */
- @WindowManagerFuncs.LidState
- int getLidState();
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 712be36e51c6..b8c5b3f5524a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -33,7 +33,6 @@ import static android.os.PowerManagerInternal.isInteractive;
import static android.os.PowerManagerInternal.wakefulnessToString;
import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -5734,11 +5733,6 @@ public final class PowerManagerService extends SystemService
@Override // Binder call
public void wakeUp(long eventTime, @WakeReason int reason, String details,
String opPackageName) {
- if (mPolicy.getLidState() == LID_CLOSED) {
- Slog.d(TAG, "Ignoring wake up call due to the lid being closed");
- return;
- }
-
final long now = mClock.uptimeMillis();
if (eventTime > now) {
Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index e82521584731..9128974fa9d3 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1004,7 +1004,8 @@ public class StatsPullAtomService extends SystemService {
}
private void initAndRegisterDeferredPullers() {
- mUwbManager = mContext.getSystemService(UwbManager.class);
+ mUwbManager = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)
+ ? mContext.getSystemService(UwbManager.class) : null;
registerUwbActivityInfo();
}
@@ -2172,6 +2173,9 @@ public class StatsPullAtomService extends SystemService {
}
private void registerUwbActivityInfo() {
+ if (mUwbManager == null) {
+ return;
+ }
int tagId = FrameworkStatsLog.UWB_ACTIVITY_INFO;
mStatsManager.setPullAtomCallback(
tagId,
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8dcb042b3733..dd62b21bc7d1 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -225,7 +225,6 @@ public class DisplayPolicy {
/** Currently it can only be non-null when physical display switch happens. */
private DecorInsets.Cache mCachedDecorInsets;
- @WindowManagerFuncs.LidState
private volatile int mLidState = LID_ABSENT;
private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private volatile boolean mHdmiPlugged;
@@ -753,11 +752,10 @@ public class DisplayPolicy {
return mNavigationBarCanMove;
}
- public void setLidState(@WindowManagerFuncs.LidState int lidState) {
+ public void setLidState(int lidState) {
mLidState = lidState;
}
- @WindowManagerFuncs.LidState
public int getLidState() {
return mLidState;
}
@@ -2532,7 +2530,10 @@ public class DisplayPolicy {
mNavBarBackgroundWindowCandidate,
mDisplayContent.mInputMethodWindow,
mNavigationBarPosition);
- final boolean drawBackground = navBackgroundWin != null;
+ final boolean drawBackground = navBackgroundWin != null
+ // There is no app window showing underneath nav bar. (e.g., The screen is locked.)
+ // Let system windows (ex: notification shade) draw nav bar background.
+ || mNavBarBackgroundWindowCandidate == null;
if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
if (drawBackground) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 79c03497223a..5ba22830eec9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -454,8 +454,8 @@ final class DevicePolicyEngine {
onGlobalPolicyChanged(policyDefinition, enforcingAdmin);
}
- applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
- policyDefinition, enforcingAdmin, /* value= */ null, /* enforcePolicy= */ true);
+ applyGlobalPolicyOnUsersWithLocalPoliciesLocked(policyDefinition, enforcingAdmin,
+ /* value= */ null, /* skipEnforcePolicy= */ false);
sendPolicyResultToAdmin(
enforcingAdmin,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7464045ad696..edb8e0c5aa64 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -14906,8 +14906,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
policy = new LockTaskPolicy(currentPolicy);
policy.setPackages(Set.of(packages));
}
- if (policy.getPackages().isEmpty()
- && policy.getFlags() == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
+ if (policy.getPackages().isEmpty()) {
mDevicePolicyEngine.removeLocalPolicy(
PolicyDefinition.LOCK_TASK,
enforcingAdmin,
@@ -20689,7 +20688,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void addUserControlDisabledPackages(CallerIdentity caller,
EnforcingAdmin enforcingAdmin, Set<String> packages) {
- if (isCallerDeviceOwner(caller)) {
+ if (isDeviceOwner(caller)) {
mDevicePolicyEngine.setGlobalPolicy(
PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
enforcingAdmin,
@@ -20705,7 +20704,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void removeUserControlDisabledPackages(CallerIdentity caller,
EnforcingAdmin enforcingAdmin) {
- if (isCallerDeviceOwner(caller)) {
+ if (isDeviceOwner(caller)) {
mDevicePolicyEngine.removeGlobalPolicy(
PolicyDefinition.USER_CONTROLLED_DISABLED_PACKAGES,
enforcingAdmin);
@@ -20717,12 +20716,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- private boolean isCallerDeviceOwner(CallerIdentity caller) {
- synchronized (getLockObject()) {
- return getDeviceOwnerUserIdUncheckedLocked() == caller.getUserId();
- }
- }
-
@Override
public List<String> getUserControlDisabledPackages(ComponentName who,
String callerPackageName) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 454337fcf141..3b048b250075 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -167,7 +167,7 @@ final class PolicyEnforcerCallbacks {
packages == null ? null : packages.stream().toList());
LocalServices.getService(UsageStatsManagerInternal.class)
.setAdminProtectedPackages(
- packages == null ? null : new ArraySet(packages), userId);
+ packages == null ? null : new ArraySet<>(packages), userId);
});
return true;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 636576492362..4989f841a275 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -276,7 +276,11 @@ public class BroadcastQueueTest {
switch (behavior) {
case SUCCESS:
case SUCCESS_PREDECESSOR:
- mQueue.onApplicationAttachedLocked(deliverRes);
+ try {
+ mQueue.onApplicationAttachedLocked(deliverRes);
+ } catch (BroadcastDeliveryFailedException e) {
+ Log.v(TAG, "Error while invoking onApplicationAttachedLocked", e);
+ }
break;
case FAIL_TIMEOUT:
case FAIL_TIMEOUT_PREDECESSOR:
@@ -1120,6 +1124,7 @@ public class BroadcastQueueTest {
final ProcessRecord restartedReceiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
getUidForPackage(PACKAGE_GREEN));
assertNotEquals(receiverApp, restartedReceiverApp);
+ verifyScheduleReceiver(restartedReceiverApp, airplane);
verifyScheduleReceiver(restartedReceiverApp, timezone);
}
@@ -1304,12 +1309,7 @@ public class BroadcastQueueTest {
final ProcessRecord receiverOrangeApp = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
getUidForPackage(PACKAGE_ORANGE));
- if (mImpl == Impl.MODERN) {
- // Modern queue does not retry sending a broadcast once any broadcast delivery fails.
- assertNull(receiverGreenApp);
- } else {
- verifyScheduleReceiver(times(1), receiverGreenApp, airplane);
- }
+ verifyScheduleReceiver(times(1), receiverGreenApp, airplane);
verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
verifyScheduleReceiver(times(1), receiverYellowApp, airplane);
verifyScheduleReceiver(times(1), receiverOrangeApp, timezone);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 5f82ec1dde02..b7dbaf93b9e2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
@@ -981,6 +982,7 @@ public class LocalDisplayAdapterTest {
DisplayDevice displayDevice = mListener.addedDisplays.get(0);
// Turn on / initialize
+ assumeTrue(displayDevice.getDisplayDeviceConfig().hasSdrToHdrRatioSpline());
Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
0);
changeStateRunnable.run();
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
index 817b245a78bf..642f54c25a46 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -60,6 +60,114 @@ public class PersistentDataStoreTest {
}
@Test
+ public void testLoadBrightness() {
+ final String uniqueDisplayId = "test:123";
+ final DisplayDevice testDisplayDevice = new DisplayDevice(
+ null, null, uniqueDisplayId, null) {
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return null;
+ }
+ };
+
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-manager-state>\n"
+ + " <display-states>\n"
+ + " <display unique-id=\"test:123\">\n"
+ + " <brightness-value user-serial=\"1\">0.1</brightness-value>\n"
+ + " <brightness-value user-serial=\"2\">0.2</brightness-value>\n"
+ + " </display>\n"
+ + " </display-states>\n"
+ + "</display-manager-state>\n";
+
+ InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mInjector.setReadStream(is);
+ mDataStore.loadIfNeeded();
+
+ float brightness = mDataStore.getBrightness(testDisplayDevice, 1);
+ assertEquals(0.1, brightness, 0.01);
+
+ brightness = mDataStore.getBrightness(testDisplayDevice, 2);
+ assertEquals(0.2, brightness, 0.01);
+ }
+
+ @Test
+ public void testSetBrightness_brightnessTagWithNoUserId_updatesToBrightnessTagWithUserId() {
+ final String uniqueDisplayId = "test:123";
+ final DisplayDevice testDisplayDevice =
+ new DisplayDevice(null, null, uniqueDisplayId, null) {
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return null;
+ }
+ };
+
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-manager-state>\n"
+ + " <display-states>\n"
+ + " <color-mode>0</color-mode>\n"
+ + " <display unique-id=\"test:123\">\n"
+ + " <brightness-value>0.5</brightness-value>\n"
+ + " </display>\n"
+ + " </display-states>\n"
+ + "</display-manager-state>\n";
+
+ InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mInjector.setReadStream(is);
+ mDataStore.loadIfNeeded();
+
+ float user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */);
+ float user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */);
+ assertEquals(0.5, user1Brightness, 0.01);
+ assertEquals(0.5, user2Brightness, 0.01);
+
+ // Override the value for user 2. Default user must have been removed.
+ mDataStore.setBrightness(testDisplayDevice, 0.2f, 2 /* userSerial */ /* brightness*/);
+
+ user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */);
+ user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */);
+ assertTrue(Float.isNaN(user1Brightness));
+ assertEquals(0.2f, user2Brightness, 0.01);
+
+ // Override the value for user 1. User-specific brightness values should co-exist.
+ mDataStore.setBrightness(testDisplayDevice, 0.1f, 1 /* userSerial */ /* brightness*/);
+ user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */);
+ user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */);
+ assertEquals(0.1f, user1Brightness, 0.01);
+ assertEquals(0.2f, user2Brightness, 0.01);
+
+ // Validate saveIfNeeded writes user-specific brightnes.
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ mInjector.setWriteStream(baos);
+ mDataStore.saveIfNeeded();
+ mTestLooper.dispatchAll();
+ assertTrue(mInjector.wasWriteSuccessful());
+ TestInjector newInjector = new TestInjector();
+ PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ newInjector.setReadStream(bais);
+ newDataStore.loadIfNeeded();
+
+ user1Brightness = newDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */);
+ user2Brightness = newDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */);
+ float unknownUserBrightness =
+ newDataStore.getBrightness(testDisplayDevice, 999 /* userSerial */);
+ assertEquals(0.1f, user1Brightness, 0.01);
+ assertEquals(0.2f, user2Brightness, 0.01);
+ assertTrue(Float.isNaN(unknownUserBrightness));
+ }
+
+ @Test
public void testLoadingBrightnessConfigurations() {
String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<display-manager-state>\n"
@@ -374,7 +482,7 @@ public class PersistentDataStoreTest {
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
newInjector.setReadStream(bais);
newDataStore.loadIfNeeded();
- assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice)));
+ assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */)));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index e6d3bbc53c83..c4f483810478 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -19,6 +19,7 @@ package com.android.server.display.brightness;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -247,6 +248,7 @@ public final class DisplayBrightnessControllerTest {
0.0f);
verify(mBrightnessChangeExecutor).execute(mOnBrightnessChangeRunnable);
verify(mBrightnessSetting).setBrightness(brightnessValue);
+ verify(mBrightnessSetting).setUserSerial(anyInt());
// Does nothing if the value is invalid
mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN);
@@ -358,4 +360,28 @@ public final class DisplayBrightnessControllerTest {
verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
verify(mBrightnessSetting, never()).setBrightness(brightness);
}
+
+ @Test
+ public void testChangeBrightnessNitsWhenUserChanges() {
+ float brightnessValue1 = 0.3f;
+ float nits1 = 200f;
+ float brightnessValue2 = 0.5f;
+ float nits2 = 300f;
+ AutomaticBrightnessController automaticBrightnessController =
+ mock(AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue1)).thenReturn(nits1);
+ when(automaticBrightnessController.convertToNits(brightnessValue2)).thenReturn(nits2);
+ mDisplayBrightnessController.setAutomaticBrightnessController(
+ automaticBrightnessController);
+
+ mDisplayBrightnessController.setBrightness(brightnessValue1, 1 /* user-serial */);
+ verify(mBrightnessSetting).setUserSerial(1);
+ verify(mBrightnessSetting).setBrightness(brightnessValue1);
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits1);
+
+ mDisplayBrightnessController.setBrightness(brightnessValue2, 2 /* user-serial */);
+ verify(mBrightnessSetting).setUserSerial(2);
+ verify(mBrightnessSetting).setBrightness(brightnessValue2);
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 7aec04568e0f..933f00231313 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -26,9 +26,6 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
@@ -148,7 +145,6 @@ public class PowerManagerServiceTest {
@Mock private ActivityManagerInternal mActivityManagerInternalMock;
@Mock private AttentionManagerInternal mAttentionManagerInternalMock;
@Mock private DreamManagerInternal mDreamManagerInternalMock;
- @Mock private WindowManagerPolicy mPolicyMock;
@Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
@Mock private Notifier mNotifierMock;
@Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@@ -209,7 +205,6 @@ public class PowerManagerServiceTest {
.thenReturn(true);
when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
- when(mPolicyMock.getLidState()).thenReturn(LID_ABSENT);
addLocalServiceMock(LightsManager.class, mLightsManagerMock);
addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
@@ -217,7 +212,6 @@ public class PowerManagerServiceTest {
addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
- addLocalServiceMock(WindowManagerPolicy.class, mPolicyMock);
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResourcesSpy = spy(mContextSpy.getResources());
@@ -684,20 +678,6 @@ public class PowerManagerServiceTest {
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
}
- @Test
- public void testWakefulnessAwake_ShouldNotWakeUpWhenLidClosed() {
- when(mPolicyMock.getLidState()).thenReturn(LID_CLOSED);
- createService();
- startSystem();
- forceSleep();
-
- mService.getBinderServiceInstance().wakeUp(mClock.now(),
- PowerManager.WAKE_REASON_POWER_BUTTON,
- "testing IPowerManager.wakeUp()", "pkg.name");
-
- assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
- }
-
/**
* Tests a series of variants that control whether a device wakes-up when it is plugged in
* or docked.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9166b3d75f60..8f7f2f66308b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -81,6 +81,7 @@ import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.No
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
@@ -678,6 +679,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@After
public void assertAllTrackersFinishedOrCancelled() {
+ waitForIdle(); // Finish async work.
// Verify that no trackers were left dangling.
for (PostNotificationTracker tracker : mPostNotificationTrackerFactory.mCreatedTrackers) {
assertThat(tracker.isOngoing()).isFalse();
@@ -11768,6 +11770,103 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertFalse(n.isUserInitiatedJob());
}
+ @Test
+ public void enqueue_updatesEnqueueRate() throws Exception {
+ Notification n = generateNotificationRecord(null).getNotification();
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId);
+ // Don't waitForIdle() here. We want to verify the "intermediate" state.
+
+ verify(mUsageStats).registerEnqueuedByApp(eq(PKG));
+ verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG));
+ verify(mUsageStats, never()).registerPostedByApp(any());
+
+ waitForIdle();
+ }
+
+ @Test
+ public void enqueue_withPost_updatesEnqueueRateAndPost() throws Exception {
+ Notification n = generateNotificationRecord(null).getNotification();
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId);
+ waitForIdle();
+
+ verify(mUsageStats).registerEnqueuedByApp(eq(PKG));
+ verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG));
+ verify(mUsageStats).registerPostedByApp(any());
+ }
+
+ @Test
+ public void enqueueNew_whenOverEnqueueRate_accepts() throws Exception {
+ Notification n = generateNotificationRecord(null).getNotification();
+ when(mUsageStats.getAppEnqueueRate(eq(PKG)))
+ .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE + 1f);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId);
+ waitForIdle();
+
+ assertThat(mService.mNotificationsByKey).hasSize(1);
+ verify(mUsageStats).registerEnqueuedByApp(eq(PKG));
+ verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG));
+ verify(mUsageStats).registerPostedByApp(any());
+ }
+
+ @Test
+ public void enqueueUpdate_whenBelowMaxEnqueueRate_accepts() throws Exception {
+ // Post the first version.
+ Notification original = generateNotificationRecord(null).getNotification();
+ original.when = 111;
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, original, mUserId);
+ waitForIdle();
+ assertThat(mService.mNotificationList).hasSize(1);
+ assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111);
+
+ reset(mUsageStats);
+ when(mUsageStats.getAppEnqueueRate(eq(PKG)))
+ .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE - 1f);
+
+ // Post the update.
+ Notification update = generateNotificationRecord(null).getNotification();
+ update.when = 222;
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, update, mUserId);
+ waitForIdle();
+
+ verify(mUsageStats).registerEnqueuedByApp(eq(PKG));
+ verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG));
+ verify(mUsageStats, never()).registerPostedByApp(any());
+ verify(mUsageStats).registerUpdatedByApp(any(), any());
+ assertThat(mService.mNotificationList).hasSize(1);
+ assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(222);
+ }
+
+ @Test
+ public void enqueueUpdate_whenAboveMaxEnqueueRate_rejects() throws Exception {
+ // Post the first version.
+ Notification original = generateNotificationRecord(null).getNotification();
+ original.when = 111;
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, original, mUserId);
+ waitForIdle();
+ assertThat(mService.mNotificationList).hasSize(1);
+ assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111);
+
+ reset(mUsageStats);
+ when(mUsageStats.getAppEnqueueRate(eq(PKG)))
+ .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE + 1f);
+
+ // Post the update.
+ Notification update = generateNotificationRecord(null).getNotification();
+ update.when = 222;
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, update, mUserId);
+ waitForIdle();
+
+ verify(mUsageStats).registerEnqueuedByApp(eq(PKG));
+ verify(mUsageStats, never()).registerEnqueuedByAppAndAccepted(any());
+ verify(mUsageStats, never()).registerPostedByApp(any());
+ verify(mUsageStats, never()).registerUpdatedByApp(any(), any());
+ assertThat(mService.mNotificationList).hasSize(1);
+ assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); // old
+ }
+
private void setDpmAppOppsExemptFromDismissal(boolean isOn) {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
index 68aa1dc1bf3b..4840f07285d6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
@@ -15,8 +15,9 @@
*/
package com.android.server.notification;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.util.concurrent.TimeUnit.HOURS;
import android.test.suitebuilder.annotation.SmallTest;
@@ -42,110 +43,120 @@ public class RateEstimatorTest extends UiServiceTestCase {
@Test
public void testRunningTimeBackwardDoesntExplodeUpdate() throws Exception {
- assertUpdateTime(mTestStartTime);
- assertUpdateTime(mTestStartTime - 1000L);
+ updateAndVerifyRate(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime - 1000L);
}
@Test
public void testRunningTimeBackwardDoesntExplodeGet() throws Exception {
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
final float rate = mEstimator.getRate(mTestStartTime - 1000L);
- assertFalse(Float.isInfinite(rate));
- assertFalse(Float.isNaN(rate));
+ assertThat(rate).isFinite();
}
@Test
public void testInstantaneousEventsDontExplodeUpdate() throws Exception {
- assertUpdateTime(mTestStartTime);
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
}
@Test
public void testInstantaneousEventsDontExplodeGet() throws Exception {
- assertUpdateTime(mTestStartTime);
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
final float rate = mEstimator.getRate(mTestStartTime);
- assertFalse(Float.isInfinite(rate));
- assertFalse(Float.isNaN(rate));
+ assertThat(rate).isFinite();
}
@Test
public void testInstantaneousBurstIsEstimatedUnderTwoPercent() throws Exception {
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
long eventStart = mTestStartTime + 1000; // start event a long time after initialization
long nextEventTime = postEvents(eventStart, 0, 5); // five events at \inf
final float rate = mEstimator.getRate(nextEventTime);
- assertLessThan("Rate", rate, 20f);
+ assertThat(rate).isLessThan(20f);
}
@Test
public void testCompactBurstIsEstimatedUnderTwoPercent() throws Exception {
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
long eventStart = mTestStartTime + 1000; // start event a long time after initialization
long nextEventTime = postEvents(eventStart, 1, 5); // five events at 1000Hz
final float rate = mEstimator.getRate(nextEventTime);
- assertLessThan("Rate", rate, 20f);
+ assertThat(rate).isLessThan(20f);
}
@Test
public void testSustained1000HzBurstIsEstimatedOverNinetyPercent() throws Exception {
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
long eventStart = mTestStartTime + 1000; // start event a long time after initialization
long nextEventTime = postEvents(eventStart, 1, 100); // one hundred events at 1000Hz
final float rate = mEstimator.getRate(nextEventTime);
- assertGreaterThan("Rate", rate, 900f);
+ assertThat(rate).isGreaterThan(900f);
}
@Test
public void testSustained100HzBurstIsEstimatedOverNinetyPercent() throws Exception {
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
long eventStart = mTestStartTime + 1000; // start event a long time after initialization
long nextEventTime = postEvents(eventStart, 10, 100); // one hundred events at 100Hz
final float rate = mEstimator.getRate(nextEventTime);
- assertGreaterThan("Rate", rate, 90f);
+ assertThat(rate).isGreaterThan(90f);
}
@Test
public void testRecoverQuicklyAfterSustainedBurst() throws Exception {
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
long eventStart = mTestStartTime + 1000; // start event a long time after initialization
- long nextEventTime = postEvents(eventStart, 10, 1000); // one hundred events at 100Hz
- final float rate = mEstimator.getRate(nextEventTime + 5000L); // two seconds later
- assertLessThan("Rate", rate, 2f);
+ long nextEventTime = postEvents(eventStart, 10, 1000); // one thousand events at 100Hz
+ final float rate = mEstimator.getRate(nextEventTime + 5000L); // five seconds later
+ assertThat(rate).isLessThan(2f);
}
@Test
public void testEstimateShouldNotOvershoot() throws Exception {
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
long eventStart = mTestStartTime + 1000; // start event a long time after initialization
- long nextEventTime = postEvents(eventStart, 1, 1000); // one thousand events at 1000Hz
+ long nextEventTime = postEvents(eventStart, 1, 5000); // five thousand events at 1000Hz
final float rate = mEstimator.getRate(nextEventTime);
- assertLessThan("Rate", rate, 1000f);
+ assertThat(rate).isAtMost(1000f);
}
@Test
public void testGetRateWithoutUpdate() throws Exception {
final float rate = mEstimator.getRate(mTestStartTime);
- assertLessThan("Rate", rate, 0.1f);
+ assertThat(rate).isLessThan(0.1f);
}
@Test
public void testGetRateWithOneUpdate() throws Exception {
- assertUpdateTime(mTestStartTime);
+ updateAndVerifyRate(mTestStartTime);
final float rate = mEstimator.getRate(mTestStartTime+1);
- assertLessThan("Rate", rate, 1f);
+ assertThat(rate).isLessThan(1f);
}
- private void assertLessThan(String label, float a, float b) {
- assertTrue(String.format("%s was %f, but should be less than %f", label, a, b), a <= b);
- }
+ @Test
+ public void testEstimateCatchesUpQuickly() {
+ long nextEventTime = postEvents(mTestStartTime, 10, 20); // 20 events at 100Hz
+
+ final float firstBurstRate = mEstimator.getRate(nextEventTime);
+ assertThat(firstBurstRate).isWithin(10f).of(100);
+
+ nextEventTime += HOURS.toMillis(3); // 3 hours later...
+ nextEventTime = postEvents(nextEventTime, 10, 20); // same burst of 20 events at 100Hz
+
+ // Catching up. Rate is not yet 100, since we had a long period of inactivity...
+ float secondBurstRate = mEstimator.getRate(nextEventTime);
+ assertThat(secondBurstRate).isWithin(10f).of(60);
- private void assertGreaterThan(String label, float a, float b) {
- assertTrue(String.format("%s was %f, but should be more than %f", label, a, b), a >= b);
+ // ... but after a few more events, we are there.
+ nextEventTime = postEvents(nextEventTime, 10, 10); // 10 more events at 100Hz
+ secondBurstRate = mEstimator.getRate(nextEventTime);
+ assertThat(secondBurstRate).isWithin(10f).of(100);
}
- /** @returns the next event time. */
+ /** @return the next event time. */
private long postEvents(long start, long dt, int num) {
long time = start;
for (int i = 0; i < num; i++) {
@@ -155,9 +166,8 @@ public class RateEstimatorTest extends UiServiceTestCase {
return time;
}
- private void assertUpdateTime(long time) {
- final float rate = mEstimator.update(time);
- assertFalse(Float.isInfinite(rate));
- assertFalse(Float.isNaN(rate));
+ private void updateAndVerifyRate(long time) {
+ mEstimator.update(time);
+ assertThat(mEstimator.getRate(time)).isFinite();
}
-}
+} \ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 192632ce0277..adf3f3976f38 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -18,8 +18,6 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -356,9 +354,4 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
public boolean isGlobalKey(int keyCode) {
return false;
}
-
- @Override
- public int getLidState() {
- return LID_ABSENT;
- }
}
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 4c55f7fa49e9..a996fa100da9 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -149,8 +149,8 @@ android_test {
name: "FlickerTestsQuickswitch",
defaults: ["FlickerTestsDefault"],
additional_manifests: ["manifests/AndroidManifestQuickswitch.xml"],
- package_name: "com.android.server.wm.flicker.rotation",
- instrumentation_target_package: "com.android.server.wm.flicker.rotation",
+ package_name: "com.android.server.wm.flicker.quickswitch",
+ instrumentation_target_package: "com.android.server.wm.flicker.quickswitch",
srcs: [
":FlickerTestsBase-src",
":FlickerTestsQuickswitch-src",
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml
index ec01317af1c2..1ede943a9fa2 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AndroidTestTemplate.xml
@@ -3,46 +3,47 @@
* Copyright 2018 Google Inc. All Rights Reserved.
-->
<configuration description="Runs WindowManager {MODULE}">
- <option name="test-tag" value="FlickerTests" />
+ <option name="test-tag" value="FlickerTests"/>
<!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
- <option name="isolated-storage" value="false" />
+ <option name="isolated-storage" value="false"/>
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
<!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on" />
+ <option name="screen-always-on" value="on"/>
<!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true" />
+ <option name="force-skip-system-props" value="true"/>
<!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all" />
+ <option name="run-command" value="cmd window tracing level all"/>
<!-- set WM tracing to frame (avoid incomplete states) -->
- <option name="run-command" value="cmd window tracing frame" />
+ <option name="run-command" value="cmd window tracing frame"/>
<!-- ensure lock screen mode is swipe -->
- <option name="run-command" value="locksettings set-disabled false" />
+ <option name="run-command" value="locksettings set-disabled false"/>
<!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
- <option name="run-command" value="pm disable com.google.android.internal.betterbug" />
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
<!-- restart launcher to activate TAPL -->
- <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
- <!-- Ensure output directory is empty at the start -->
- <option name="run-command" value="rm -rf /sdcard/flicker" />
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
<!-- Increase trace size: 20mb for WM and 80mb for SF -->
- <option name="run-command" value="cmd window tracing size 20480" />
- <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920" />
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
- <option name="run-command" value="settings put system show_touches 1" />
- <option name="run-command" value="settings put system pointer_location 1" />
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
<option name="teardown-command"
- value="settings delete secure show_ime_with_hard_keyboard" />
- <option name="teardown-command" value="settings delete system show_touches" />
- <option name="teardown-command" value="settings delete system pointer_location" />
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
<option name="teardown-command"
- value="cmd overlay enable com.android.internal.systemui.navbar.gestural" />
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="{MODULE}.apk"/>
- <option name="test-file-name" value="FlickerTestApp.apk" />
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
</target_preparer>
<!-- Needed for pushing the trace config file -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
@@ -53,30 +54,38 @@
/>
<!--Install the content provider automatically when we push some file in sdcard folder.-->
<!--Needed to avoid the installation during the test suite.-->
- <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto" />
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="{PACKAGE}"/>
- <option name="shell-timeout" value="6600s" />
- <option name="test-timeout" value="6600s" />
- <option name="hidden-api-checks" value="false" />
- <option name="device-listeners" value="android.device.collectors.PerfettoListener" />
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
<!-- PerfettoListener related arguments -->
- <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
<option name="instrumentation-arg"
key="perfetto_config_file"
value="trace_config.textproto"
/>
- <option name="instrumentation-arg" key="per_run" value="true" />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="pull-pattern-keys" value="perfetto_file_path" />
- </metrics_collector>
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="pull-pattern-keys" value="(\w)+\.winscope" />
- <option name="pull-pattern-keys" value="(\w)+\.mp4" />
- <option name="collect-on-run-ended-only" value="false" />
- <option name="clean-up" value="true" />
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.close/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
</metrics_collector>
</configuration>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
index e6594c969373..a87fae857509 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
@@ -57,7 +57,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ActivitiesTransitionTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class ActivityTransitionTest(flicker: FlickerTest) : BaseTest(flicker) {
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
index ac05c7687311..85344a156d7a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
@@ -27,7 +27,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppAfterCameraTestCfArm(flicker: FlickerTest) : OpenAppAfterCameraTest(flicker) {
+class ActivityTransitionTestCfArm(flicker: FlickerTest) : ActivityTransitionTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
index 3a80c6649833..575206591e59 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
@@ -55,7 +55,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIconColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
index d33a2724ca44..d453c1ae908a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
@@ -32,7 +32,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdFromIconCfArm(flicker: FlickerTest) : OpenAppColdFromIcon(flicker) {
+class OpenAppFromIconColdTestCfArm(flicker: FlickerTest) : OpenAppFromIconColdTest(flicker) {
@Test
@FlakyTest
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
index 549183f407e2..e74731555642 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
@@ -38,7 +38,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppAfterCameraTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentColdAfterCameraTest(flicker: FlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
private val cameraApp = CameraAppHelper(instrumentation)
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
index 43d28fa60e51..177ad7dc09d1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
@@ -27,8 +27,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromNotificationWarmCfArm(flicker: FlickerTest) :
- OpenAppFromNotificationWarm(flicker) {
+class OpenAppFromIntentColdAfterCameraTestCfArm(flicker: FlickerTest) :
+ OpenAppFromIntentColdAfterCameraTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index 26f88d23cda0..f45f728664cf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -58,7 +58,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentColdTest(flicker: FlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
index d9a99dadbd3d..0d695f306c00 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
@@ -31,7 +31,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdTestCfArm(flicker: FlickerTest) : OpenAppColdTest(flicker) {
+class OpenAppFromIntentColdTestCfArm(flicker: FlickerTest) : OpenAppFromIntentColdTest(flicker) {
@FlakyTest(bugId = 273696733)
@Test
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 3385830ee77f..a42bff5bf170 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -58,7 +58,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppWarmTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentWarmTest(flicker: FlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
index d8b38b30cf13..b6ffcb3df9f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
@@ -29,7 +29,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTestCfArm(flicker: FlickerTest) : OpenAppWarmTest(flicker) {
+class OpenAppFromIntentWarmTestCfArm(flicker: FlickerTest) : OpenAppFromIntentWarmTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt
index b21777b30b21..fd4272600d55 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt
@@ -44,8 +44,8 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-open class OpenAppFromLockNotificationCold(flicker: FlickerTest) :
- OpenAppFromNotificationCold(flicker) {
+open class OpenAppFromLockscreenNotificationColdTest(flicker: FlickerTest) :
+ OpenAppFromNotificationColdTest(flicker) {
override val openingNotificationsFromLockScreen = true
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt
index ec92ca65f80a..fd051d50d032 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -46,7 +46,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromLockNotificationWarm(flicker: FlickerTest) : OpenAppFromNotificationWarm(flicker) {
+class OpenAppFromLockscreenNotificationWarmTest(flicker: FlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
override val openingNotificationsFromLockScreen = true
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index 009d61797fe0..37afa8d0caba 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -37,6 +37,8 @@ import org.junit.runners.Parameterized
* Test cold launching an app from a notification from the lock screen when there is an app overlaid
* on the lock screen.
*
+ * This test assumes the device doesn't have AOD enabled
+ *
* To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp`
*/
@RequiresDevice
@@ -44,8 +46,8 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-class OpenAppFromLockNotificationWithLockOverlayApp(flicker: FlickerTest) :
- OpenAppFromLockNotificationCold(flicker) {
+class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: FlickerTest) :
+ OpenAppFromLockscreenNotificationColdTest(flicker) {
private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation)
// Although we are technically still locked here, the overlay app means we should open the
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
index eae9ca10c711..30c3ec205bb1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
@@ -28,7 +28,7 @@ import org.junit.Ignore
import org.junit.Test
/** Base class for app launch tests from lock screen */
-abstract class OpenAppFromLockTransition(flicker: FlickerTest) : OpenAppTransition(flicker) {
+abstract class OpenAppFromLockscreenTransition(flicker: FlickerTest) : OpenAppTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 1383ae39f760..924d03f6d302 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -63,7 +63,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppNonResizeableTest(flicker: FlickerTest) : OpenAppFromLockTransition(flicker) {
+open class OpenAppFromLockscreenViaIntentTest(flicker: FlickerTest) :
+ OpenAppFromLockscreenTransition(flicker) {
override val testApp = NonResizeableAppHelper(instrumentation)
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt
index 7bcb91070ecf..d873ec5e83e2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt
@@ -35,8 +35,6 @@ import org.junit.runners.Parameterized
/**
* Test cold launching an app from a notification.
*
- * This test assumes the device doesn't have AOD enabled
- *
* To run this test: `atest FlickerTests:OpenAppFromNotificationCold`
*/
@RequiresDevice
@@ -44,8 +42,8 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-open class OpenAppFromNotificationCold(flicker: FlickerTest) :
- OpenAppFromNotificationWarm(flicker) {
+open class OpenAppFromNotificationColdTest(flicker: FlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt
index 8b4a613305c0..fb2a48c2ad7f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt
@@ -29,8 +29,8 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-class OpenAppFromNotificationColdCfArm(flicker: FlickerTest) :
- OpenAppFromNotificationCold(flicker) {
+class OpenAppFromNotificationColdTestCfArm(flicker: FlickerTest) :
+ OpenAppFromNotificationColdTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt
index 425e674dec3a..99668ecd0b68 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt
@@ -47,15 +47,13 @@ import org.junit.runners.Parameterized
/**
* Test cold launching an app from a notification.
*
- * This test assumes the device doesn't have AOD enabled
- *
* To run this test: `atest FlickerTests:OpenAppFromNotificationWarm`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromNotificationWarm(flicker: FlickerTest) : OpenAppTransition(flicker) {
+open class OpenAppFromNotificationWarmTest(flicker: FlickerTest) : OpenAppTransition(flicker) {
override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation)
open val openingNotificationsFromLockScreen = false
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt
index 8b89a8b4c40d..2a2597e1ebe8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt
@@ -27,7 +27,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ActivitiesTransitionTestCfArm(flicker: FlickerTest) : ActivitiesTransitionTest(flicker) {
+class OpenAppFromNotificationWarmTestCfArm(flicker: FlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index ae9ca8007dc8..6ee8ae69924a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -60,7 +60,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) :
+class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: FlickerTest) :
OpenAppFromLauncherTransition(flicker) {
private val cameraApp = CameraAppHelper(instrumentation)
override val testApp: StandardAppHelper
diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml
index 62eee0270cac..e890c9974882 100644
--- a/tests/InputMethodStressTest/AndroidManifest.xml
+++ b/tests/InputMethodStressTest/AndroidManifest.xml
@@ -19,8 +19,7 @@
package="com.android.inputmethod.stresstest">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application>
- <activity android:name=".ImeStressTestUtil$TestActivity"
- android:configChanges="orientation|screenSize"/>
+ <activity android:name=".ImeStressTestUtil$TestActivity"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
index 7632ab08b655..b76a4eb8c0e6 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
@@ -493,6 +493,7 @@ public final class ImeOpenCloseStressTest {
verifyShowBehavior(activity);
}
+ // TODO: Add tests for activities that don't handle the rotation.
@Test
public void testRotateScreenWithKeyboardOn() throws Exception {
Intent intent =
@@ -514,14 +515,14 @@ public final class ImeOpenCloseStressTest {
Thread.sleep(1000);
Log.i(TAG, "Rotate screen right");
assertThat(uiDevice.isNaturalOrientation()).isFalse();
- verifyShowBehavior(activity);
+ verifyRotateBehavior(activity);
uiDevice.setOrientationLeft();
uiDevice.waitForIdle();
Thread.sleep(1000);
Log.i(TAG, "Rotate screen left");
assertThat(uiDevice.isNaturalOrientation()).isFalse();
- verifyShowBehavior(activity);
+ verifyRotateBehavior(activity);
uiDevice.setOrientationNatural();
uiDevice.waitForIdle();
@@ -569,4 +570,36 @@ public final class ImeOpenCloseStressTest {
waitOnMainUntilImeIsShown(editText);
}
}
+
+ private static void verifyRotateBehavior(TestActivity activity) {
+ // Get the new TestActivity after recreation.
+ TestActivity newActivity = TestActivity.getLastCreatedInstance();
+ assertThat(newActivity).isNotNull();
+ assertThat(newActivity).isNotEqualTo(activity);
+
+ EditText newEditText = newActivity.getEditText();
+ int softInputMode = newActivity.getWindow().getAttributes().softInputMode;
+ int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+
+ if (hasUnfocusableWindowFlags(newActivity)) {
+ verifyImeAlwaysHiddenWithWindowFlagSet(newActivity);
+ return;
+ }
+
+ if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
+ // After rotation, the keyboard would be hidden only when the flag is
+ // SOFT_INPUT_STATE_ALWAYS_HIDDEN. However, SOFT_INPUT_STATE_HIDDEN is different because
+ // it requires appending SOFT_INPUT_IS_FORWARD_NAVIGATION flag, which won't be added
+ // when rotating the devices (rotating doesn't navigate forward to the next app window.)
+ verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/
+ true);
+ waitOnMainUntilImeIsHidden(newEditText);
+
+ } else {
+ // Other cases, keyboard would be shown.
+ verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/
+ true);
+ waitOnMainUntilImeIsShown(newEditText);
+ }
+ }
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index e16c9152df3e..f3c81942ae42 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -45,6 +45,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.ThrowingRunnable;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
@@ -296,6 +297,8 @@ public final class ImeStressTestUtil {
private static final String TAG = "ImeStressTestUtil.TestActivity";
private EditText mEditText;
private boolean mIsAnimating;
+ private static WeakReference<TestActivity> sLastCreatedInstance =
+ new WeakReference<>(null);
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -336,6 +339,7 @@ public final class ImeStressTestUtil {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
+ sLastCreatedInstance = new WeakReference<>(this);
boolean isUnfocusableView = getIntent().getBooleanExtra(UNFOCUSABLE_VIEW, false);
boolean requestFocus = getIntent().getBooleanExtra(REQUEST_FOCUS_ON_CREATE, false);
int softInputFlags = getIntent().getIntExtra(SOFT_INPUT_FLAGS, 0);
@@ -378,6 +382,12 @@ public final class ImeStressTestUtil {
}
}
+ /** Get the last created TestActivity instance. */
+ @Nullable
+ public static TestActivity getLastCreatedInstance() {
+ return sLastCreatedInstance.get();
+ }
+
/** Show IME with InputMethodManager. */
public boolean showImeWithInputMethodManager() {
boolean showResult =
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index 039bb4e2788c..46745e995f64 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -11,7 +11,7 @@ java_library_host {
name: "protologtool-lib",
srcs: [
"src/com/android/protolog/tool/**/*.kt",
- ":protolog-common-src",
+ ":protolog-common-no-android-src",
],
static_libs: [
"javaparser",