Merge "Fill in a stub parameter to startKeyguardExit" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 7d9e95b..4e34b63 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -172,6 +172,7 @@
// Window
aconfig_declarations {
name: "com.android.window.flags.window-aconfig",
+ exportable: true,
package: "com.android.window.flags",
container: "system",
srcs: ["core/java/android/window/flags/*.aconfig"],
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 2587969..5f32ba0 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -43,29 +43,14 @@
],
out: [
"ravenwood.jar",
-
- // Following files are created just as FYI.
- "hoststubgen_framework-minus-apex_keep_all.txt",
- "hoststubgen_framework-minus-apex_dump.txt",
-
"hoststubgen_framework-minus-apex.log",
- "hoststubgen_framework-minus-apex_stats.csv",
- "hoststubgen_framework-minus-apex_apis.csv",
],
}
framework_minus_apex_cmd = "$(location hoststubgen) " +
"@$(location :ravenwood-standard-options) " +
-
"--debug-log $(location hoststubgen_framework-minus-apex.log) " +
- "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " +
- "--supported-api-list-file $(location hoststubgen_framework-minus-apex_apis.csv) " +
-
"--out-impl-jar $(location ravenwood.jar) " +
-
- "--gen-keep-all-file $(location hoststubgen_framework-minus-apex_keep_all.txt) " +
- "--gen-input-dump-file $(location hoststubgen_framework-minus-apex_dump.txt) " +
-
"--in-jar $(location :framework-minus-apex-for-hoststubgen) " +
"--policy-override-file $(location :ravenwood-framework-policies) " +
"--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) "
@@ -133,13 +118,26 @@
// Build framework-minus-apex.ravenwood-base without sharding.
// We extract the various dump files from this one, rather than the sharded ones, because
// some dumps use the output from other classes (e.g. base classes) which may not be in the
-// same shard.
+// same shard. Also some of the dump files ("apis") may be slow even when sharded, because
+// the output contains the information from all the input classes, rather than the output classes.
// Not using sharding is fine for this module because it's only used for collecting the
// dump / stats files, which don't have to happen regularly.
java_genrule {
name: "framework-minus-apex.ravenwood-base_all",
defaults: ["framework-minus-apex.ravenwood-base_defaults"],
- cmd: framework_minus_apex_cmd,
+ cmd: framework_minus_apex_cmd +
+ "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " +
+ "--supported-api-list-file $(location hoststubgen_framework-minus-apex_apis.csv) " +
+
+ "--gen-keep-all-file $(location hoststubgen_framework-minus-apex_keep_all.txt) " +
+ "--gen-input-dump-file $(location hoststubgen_framework-minus-apex_dump.txt) ",
+
+ out: [
+ "hoststubgen_framework-minus-apex_keep_all.txt",
+ "hoststubgen_framework-minus-apex_dump.txt",
+ "hoststubgen_framework-minus-apex_stats.csv",
+ "hoststubgen_framework-minus-apex_apis.csv",
+ ],
}
// Marge all the sharded jars
diff --git a/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java b/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
index c00c8d5..06cd942 100644
--- a/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
+++ b/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
@@ -36,7 +36,7 @@
@RunWith(AndroidJUnit4.class)
public final class SettingsProviderPerfTest {
- private static final String NAMESPACE = "test@namespace";
+ private static final String NAMESPACE = "testing";
private static final String SETTING_NAME1 = "test:setting1";
private static final String SETTING_NAME2 = "test-setting2";
private static final String UNSET_SETTING = "test_unset_setting";
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 18ee6f2..ba66ff7 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -4441,6 +4441,11 @@
public void run() {
ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
+ synchronized (mLock) {
+ mLastTimeChangeClockTime = mInjector.getCurrentTimeMillis();
+ mLastTimeChangeRealtime = mInjector.getElapsedRealtimeMillis();
+ }
+
while (true) {
int result = mInjector.waitForAlarm();
final long nowRTC = mInjector.getCurrentTimeMillis();
@@ -4464,10 +4469,9 @@
expectedClockTime = lastTimeChangeClockTime
+ (nowELAPSED - mLastTimeChangeRealtime);
}
- if (lastTimeChangeClockTime == 0 || nowRTC < (expectedClockTime - 1000)
+ if (nowRTC < (expectedClockTime - 1000)
|| nowRTC > (expectedClockTime + 1000)) {
- // The change is by at least +/- 1000 ms (or this is the first change),
- // let's do it!
+ // The change is by at least +/- 1000 ms, let's do it!
if (DEBUG_BATCH) {
Slog.v(TAG, "Time changed notification from kernel; rebatching");
}
diff --git a/config/Android.bp b/config/Android.bp
index c9948c3..4a61cc7 100644
--- a/config/Android.bp
+++ b/config/Android.bp
@@ -43,6 +43,6 @@
prebuilt_etc {
name: "dirty-image-objects",
- src: "dirty-image-objects",
- filename: "dirty-image-objects",
+ src: "dirty-image-objects.txt",
+ filename: "dirty-image-objects.txt",
}
diff --git a/config/OWNERS b/config/OWNERS
index 6a5df76..916bf67d 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -2,7 +2,7 @@
# art-team@ manages the boot image profiles
per-file boot-* = islamelbanna@google.com, ngeoffray@google.com, vmarko@google.com
-per-file dirty-image-objects = ishcheikin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file dirty-image-objects.txt = ishcheikin@google.com, ngeoffray@google.com, vmarko@google.com
per-file generate-preloaded-classes.sh = islamelbanna@google.com, ngeoffray@google.com, vmarko@google.com
per-file preloaded-classes* = islamelbanna@google.com, ngeoffray@google.com, vmarko@google.com
diff --git a/config/README.md b/config/README.md
index 450a5c6..0d9cbb6 100644
--- a/config/README.md
+++ b/config/README.md
@@ -5,7 +5,7 @@
* boot-profile.txt: An ordered list of methods from the boot classpath to be compiled by
the JIT in the order provided in the file. Used by JIT zygote, when on-device
signing failed.
-* dirty-image-objects: List of objects in the boot image which are known to
+* dirty-image-objects.txt: List of objects in the boot image which are known to
become dirty. This helps binning objects in the image file.
* preloaded-classes: classes that will be allocated in the boot image, and
initialized by the zygote.
diff --git a/config/dirty-image-objects b/config/dirty-image-objects
deleted file mode 100644
index f2e2b82..0000000
--- a/config/dirty-image-objects
+++ /dev/null
@@ -1,1728 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-#
-#
-# Dirty-image-objects file for boot image.
-# The image writer will bin these objects together in the image.
-# More info about dirty objects format and how to collect the data can be
-# found in: art/imgdiag/dirty_image_objects.md
-# This particular file was generated by dumping all pre-installed apps.
-#
-Landroid/text/style/URLSpan; 0
-Landroid/content/res/Resources$NotFoundException; 1
-Landroid/os/PowerManager$WakeLock; 2
-Landroid/os/BatterySaverPolicyConfig; 2
-Landroid/content/ContextWrapper; 2
-Landroid/app/WallpaperInfo; 2
-Landroid/content/pm/PackageManager; 2
-Landroid/app/IWallpaperManager; 2
-Ljava/lang/BootClassLoader; 2
-Ljava/time/Duration; 2
-Landroid/util/Printer; 2
-Landroid/app/WallpaperManager$OnColorsChangedListener; 2
-Landroid/app/WallpaperColors; 2
-Landroid/content/pm/ServiceInfo; 2
-Landroid/app/KeyguardManager$KeyguardDismissCallback; 2
-Ljava/lang/CharSequence; 3
-Landroid/widget/Switch; 4
-Lcom/android/internal/util/ContrastColorUtil; 4
-Landroid/view/SurfaceControl; 4
-Landroid/graphics/ColorMatrix;.dexCache:Ljava/lang/Object; 4
-Lcom/android/internal/widget/CachingIconView; 4
-Landroid/window/IRemoteTransition$Stub$Proxy; 4
-Landroid/app/trust/TrustManager$TrustListener; 4
-Landroid/view/NotificationHeaderView; 4
-Lcom/android/internal/widget/ImageResolver; 4
-Landroid/window/WindowContainerTransaction$Change; 4
-Lcom/android/internal/widget/MessagingLayout; 4
-Ljava/util/concurrent/ConcurrentLinkedQueue; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry; 4
-Landroid/view/RemotableViewMethod; 4
-Landroid/app/IApplicationThread$Stub$Proxy; 4
-Landroid/os/FileUtils; 4
-Landroid/view/View;.SCALE_X:Landroid/util/Property; 4
-Landroid/widget/GridLayout;.UNDEFINED_ALIGNMENT:Landroid/widget/GridLayout$Alignment; 4
-Landroid/media/MediaPlayer$EventHandler; 4
-Landroid/widget/DateTimeView; 4
-Llibcore/util/ZoneInfo; 4
-Lcom/android/internal/statusbar/IStatusBarService; 4
-Ljava/lang/invoke/MethodType;.internTable:Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet;.stale:Ljava/lang/ref/ReferenceQueue; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry; 4
-Lcom/android/internal/logging/UiEventLogger; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry; 4
-Landroid/renderscript/RenderScript; 4
-Landroid/view/ViewTreeObserver$OnWindowVisibilityChangeListener; 4
-Lcom/android/internal/widget/RemeasuringLinearLayout; 4
-Landroid/widget/DateTimeView$ReceiverInfo$1; 4
-Landroid/view/View;.TRANSLATION_Y:Landroid/util/Property; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry; 4
-Lcom/android/internal/widget/NotificationExpandButton; 4
-Lcom/android/internal/view/menu/ActionMenuItemView; 4
-Landroid/view/animation/AnimationSet; 4
-Landroid/hardware/biometrics/BiometricSourceType;.FINGERPRINT:Landroid/hardware/biometrics/BiometricSourceType; 4
-Landroid/window/WindowOrganizer;.IWindowOrganizerControllerSingleton:Landroid/util/Singleton; 4
-Ljava/lang/Runnable; 4
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;.mHandlerMap:Ljava/util/HashMap; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry; 4
-Lcom/android/internal/widget/ImageFloatingTextView; 4
-Landroid/window/IWindowContainerToken$Stub$Proxy; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry; 4
-Landroid/content/res/ColorStateList; 4
-Landroid/view/View;.SCALE_Y:Landroid/util/Property; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap; 4
-Lcom/android/internal/widget/ConversationLayout; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry; 4
-Lcom/android/internal/colorextraction/ColorExtractor$OnColorsChangedListener; 4
-Landroid/hardware/face/FaceManager$FaceDetectionCallback; 4
-Landroid/widget/RemoteViews;.sLookupKey:Landroid/widget/RemoteViews$MethodKey; 4
-Landroid/widget/ViewSwitcher;.dexCache:Ljava/lang/Object; 4
-Lcom/android/internal/widget/NotificationActionListLayout; 4
-Ljava/util/concurrent/ConcurrentLinkedQueue$Node; 4
-Landroid/hardware/biometrics/BiometricSourceType;.FACE:Landroid/hardware/biometrics/BiometricSourceType; 4
-Landroid/hardware/biometrics/BiometricSourceType;.IRIS:Landroid/hardware/biometrics/BiometricSourceType; 4
-Landroid/view/NotificationTopLineView; 4
-Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry; 4
-Landroid/widget/RemoteViews;.sMethods:Landroid/util/ArrayMap; 4
-Lcom/android/internal/os/BinderInternal$BinderProxyLimitListener; 5
-Landroid/app/AppOpsManager$OnOpNotedInternalListener; 5
-Lcom/android/internal/R$styleable;.WindowAnimation:[I 5
-Lcom/android/internal/logging/UiEventLogger$UiEventEnum; 5
-Lcom/android/internal/policy/AttributeCache; 5
-Landroid/app/Notification$CallStyle; 5
-Landroid/app/AppOpsManager$OnOpNotedListener; 5
-Lcom/android/internal/protolog/BaseProtoLogImpl; 5
-Landroid/app/AppOpsManager$OnOpStartedListener; 5
-Lcom/android/internal/util/ScreenshotHelper$1; 5
-Landroid/app/Notification$DecoratedCustomViewStyle; 5
-Landroid/view/DisplayCutout; 5
-Landroid/view/InputEvent;.mNextSeq:Ljava/util/concurrent/atomic/AtomicInteger; 5
-Lcom/android/internal/statusbar/NotificationVisibility; 5
-Landroid/telephony/DataSpecificRegistrationInfo; 6
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle; 7
-Landroid/telephony/VoiceSpecificRegistrationInfo; 8
-Landroid/telephony/AnomalyReporter; 8
-Landroid/telephony/TelephonyRegistryManager;.sCarrierPrivilegeCallbacks:Ljava/util/WeakHashMap; 8
-Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry; 8
-Landroid/app/LoadedApk$ServiceDispatcher$InnerConnection; 8
-Landroid/content/ContentProvider$Transport; 8
-Landroid/telephony/NetworkRegistrationInfo; 8
-Landroid/net/MatchAllNetworkSpecifier; 8
-Landroid/telephony/TelephonyRegistryManager;.sCarrierPrivilegeCallbacks:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry; 8
-Landroid/app/PropertyInvalidatedCache;.sInvalidates:Ljava/util/HashMap; 9
-Landroid/app/PropertyInvalidatedCache$NoPreloadHolder; 9
-Landroid/app/PropertyInvalidatedCache;.sDisabledKeys:Ljava/util/HashSet;.map:Ljava/util/HashMap; 10
-Landroid/media/AudioSystem$AudioRecordingCallback; 11
-Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedString; 11
-Landroid/net/metrics/IpManagerEvent; 11
-Lcom/android/internal/os/ProcessCpuTracker$FilterStats; 11
-Lcom/android/internal/infra/AbstractRemoteService$AsyncRequest; 11
-Landroid/content/pm/RegisteredServicesCache$3; 11
-Lcom/android/internal/os/LooperStats; 11
-Lcom/android/server/AppWidgetBackupBridge; 11
-Landroid/hardware/display/DisplayManagerInternal; 11
-Landroid/content/pm/PackageInfo; 11
-Landroid/hardware/soundtrigger/SoundTriggerModule$EventHandlerDelegate; 11
-Landroid/app/servertransaction/ResumeActivityItem; 11
-Lcom/android/internal/widget/AlertDialogLayout; 11
-Landroid/content/pm/FallbackCategoryProvider;.sFallbacks:Landroid/util/ArrayMap; 11
-Landroid/os/RemoteCallback$1; 11
-Landroid/content/pm/SharedLibraryInfo; 11
-Landroid/util/MemoryIntArray; 11
-Landroid/net/metrics/DhcpErrorEvent; 11
-Lcom/android/internal/util/function/DodecConsumer; 11
-Landroid/provider/Settings; 11
-Landroid/app/PropertyInvalidatedCache;.sCorkLock:Ljava/lang/Object; 11
-Lcom/android/internal/os/CachedDeviceState$Readonly; 11
-Landroid/app/job/JobServiceEngine$JobHandler; 11
-Landroid/app/SystemServiceRegistry; 11
-Lcom/android/internal/os/BinderInternal$CallStatsObserver; 11
-Lcom/android/internal/statusbar/IStatusBar$Stub$Proxy; 11
-Landroid/hardware/location/IActivityRecognitionHardwareClient; 11
-Landroid/telecom/Logging/EventManager$EventListener; 11
-Landroid/accounts/AccountManagerInternal; 11
-Lcom/android/internal/os/KernelCpuBpfTracking; 11
-Lcom/android/internal/statusbar/NotificationVisibility$NotificationLocation; 11
-Landroid/hardware/camera2/CameraManager$CameraManagerGlobal; 11
-Landroid/os/ServiceSpecificException; 11
-Landroid/net/Uri$PathPart;.NULL:Landroid/net/Uri$PathPart; 11
-Landroid/app/ActivityManagerInternal; 11
-Landroid/media/AudioSystem; 11
-Landroid/service/dreams/DreamManagerInternal; 11
-Landroid/debug/AdbManagerInternal; 11
-Landroid/graphics/Bitmap$CompressFormat; 11
-Landroid/hardware/location/NanoAppMessage; 11
-Landroid/os/storage/StorageManagerInternal; 11
-Landroid/app/AppOpsManagerInternal; 11
-Ljava/security/cert/CertificateException; 11
-Ldalvik/system/VMRuntime; 11
-Landroid/content/pm/SigningInfo; 11
-Landroid/view/KeyEvent; 11
-Lcom/android/internal/view/WindowManagerPolicyThread; 11
-Landroid/graphics/Region;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 11
-Landroid/content/res/ResourceTimer; 11
-Landroid/view/autofill/AutofillManagerInternal; 11
-Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object; 11
-Landroid/graphics/Region;.sPool:Landroid/util/Pools$SynchronizedPool; 11
-Landroid/app/LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0; 11
-Lcom/android/server/LocalServices;.sLocalServiceObjects:Landroid/util/ArrayMap; 11
-Landroid/app/admin/DevicePolicyManagerInternal$OnCrossProfileWidgetProvidersChangeListener; 11
-Landroid/accounts/AccountManagerInternal$OnAppPermissionChangeListener; 11
-Landroid/content/pm/PermissionInfo; 11
-Landroid/view/WindowManagerPolicyConstants$PointerEventListener; 11
-Landroid/os/UEventObserver; 11
-Landroid/media/AudioManagerInternal$RingerModeDelegate; 11
-Landroid/view/Display$HdrCapabilities; 11
-Landroid/service/notification/Condition; 11
-Landroid/content/pm/UserPackage; 11
-Landroid/app/AppOpsManager$SamplingStrategy; 11
-Landroid/telephony/ServiceState; 11
-Landroid/app/servertransaction/PauseActivityItem; 11
-Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sMessageCallbacksPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool;.mLock:Ljava/lang/Object; 11
-Landroid/view/KeyCharacterMap$FallbackAction; 11
-Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringArray; 11
-Landroid/hardware/display/DeviceProductInfo; 11
-Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap;.mHashes:[I 11
-Landroid/content/pm/RegisteredServicesCache$2; 11
-Landroid/content/pm/PackageManager;.sCacheAutoCorker:Landroid/app/PropertyInvalidatedCache$AutoCorker; 11
-Landroid/app/PropertyInvalidatedCache;.sCorks:Ljava/util/HashMap; 11
-Landroid/service/notification/StatusBarNotification; 11
-Landroid/app/servertransaction/ConfigurationChangeItem; 11
-Landroid/app/ActivityManager$RecentTaskInfo; 11
-Landroid/app/Notification; 11
-Landroid/app/servertransaction/DestroyActivityItem; 11
-Landroid/webkit/WebViewLibraryLoader$RelroFileCreator; 11
-Landroid/net/metrics/NetworkEvent; 11
-Landroid/media/AudioPlaybackConfiguration; 11
-Landroid/accessibilityservice/AccessibilityServiceInfo; 11
-Landroid/hardware/display/DeviceProductInfo$ManufactureDate; 11
-Landroid/os/storage/StorageVolume; 11
-Landroid/os/BatteryManagerInternal; 11
-Landroid/appwidget/AppWidgetManagerInternal; 11
-Landroid/app/servertransaction/NewIntentItem; 11
-Landroid/content/pm/ShortcutServiceInternal; 11
-Landroid/app/assist/ActivityId; 11
-Landroid/window/DisplayAreaAppearedInfo; 11
-Landroid/os/Process;.ZYGOTE_PROCESS:Landroid/os/ZygoteProcess;.mLock:Ljava/lang/Object; 11
-Landroid/app/usage/UsageStats; 11
-Landroid/app/Notification$MediaStyle; 11
-Landroid/media/AudioSystem$DynamicPolicyCallback; 11
-Landroid/content/pm/ProviderInfo; 11
-Landroid/os/PowerManagerInternal; 11
-Landroid/service/voice/VoiceInteractionManagerInternal; 11
-Landroid/content/pm/FeatureInfo; 11
-Landroid/app/servertransaction/TopResumedActivityChangeItem; 11
-Landroid/app/Notification$DecoratedMediaCustomViewStyle; 11
-Landroid/appwidget/AppWidgetProviderInfo; 11
-Landroid/app/AppOpsManager$NoteOpEvent; 11
-Landroid/graphics/GraphicsStatsService; 11
-Landroid/view/DisplayAddress$Physical; 11
-Landroid/content/ComponentName$WithComponentName; 11
-Landroid/app/admin/DevicePolicyManagerInternal; 11
-Landroid/os/ResultReceiver$MyResultReceiver; 11
-Landroid/content/ContentProviderClient; 11
-Landroid/content/pm/RegisteredServicesCache$1; 11
-Landroid/app/PendingIntent$FinishedDispatcher; 11
-Landroid/location/LocationManager; 11
-Landroid/hardware/location/ContextHubInfo; 11
-Landroid/content/pm/ShortcutServiceInternal$ShortcutChangeListener; 11
-Lcom/android/server/usage/AppStandbyInternal; 11
-Landroid/content/pm/RegisteredServicesCacheListener; 11
-Landroid/app/servertransaction/LaunchActivityItem; 11
-Landroid/content/pm/BaseParceledListSlice$1; 11
-Landroid/annotation/StringRes; 11
-Lcom/android/internal/R$styleable;.Window:[I 11
-Landroid/service/notification/ZenModeConfig; 11
-Landroid/telecom/Logging/SessionManager$ISessionListener; 11
-Landroid/app/time/TimeZoneConfiguration; 11
-Landroid/net/metrics/ValidationProbeEvent; 11
-Landroid/content/pm/PackageInstaller$SessionInfo; 11
-Landroid/content/pm/UserPackage;.sCache:Landroid/util/SparseArrayMap;.mData:Landroid/util/SparseArray; 11
-Landroid/content/pm/PermissionGroupInfo; 11
-Landroid/hardware/sidekick/SidekickInternal; 11
-Lcom/android/internal/widget/ButtonBarLayout; 11
-Landroid/content/pm/LauncherActivityInfoInternal; 11
-Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap; 11
-Lcom/android/internal/widget/LockSettingsInternal; 11
-Landroid/media/AudioManagerInternal; 11
-Landroid/app/AppOpsManager$AttributedOpEntry; 11
-Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringList; 11
-Landroid/telecom/Log; 11
-Landroid/app/time/TimeZoneCapabilities; 11
-Landroid/attention/AttentionManagerInternal; 11
-Landroid/view/WindowManagerPolicyConstants; 11
-Landroid/content/pm/CrossProfileAppsInternal; 11
-Landroid/hardware/location/GeofenceHardwareService; 11
-Landroid/content/pm/dex/ArtManagerInternal; 11
-Landroid/net/metrics/IpReachabilityEvent; 11
-Landroid/content/pm/LauncherApps$ShortcutQuery$QueryFlags; 11
-Landroid/media/AudioAttributes; 11
-Landroid/app/PropertyInvalidatedCache$AutoCorker$1; 11
-Landroid/net/metrics/ApfProgramEvent; 11
-Landroid/content/pm/SigningDetails; 11
-Lcom/android/internal/protolog/ProtoLogImpl; 11
-Landroid/hardware/biometrics/ComponentInfoInternal; 11
-Lcom/android/internal/util/ToBooleanFunction; 11
-Landroid/app/ActivityThread$H; 11
-Landroid/hardware/location/GeofenceHardwareImpl; 11
-Landroid/net/wifi/nl80211/WifiNl80211Manager$ScanEventHandler; 11
-Landroid/util/NtpTrustedTime; 11
-Landroid/hardware/soundtrigger/SoundTrigger$StatusListener; 11
-Lcom/android/internal/app/procstats/AssociationState;.sTmpSourceKey:Lcom/android/internal/app/procstats/AssociationState$SourceKey; 11
-Ljava/util/zip/ZipFile$ZipFileInflaterInputStream; 11
-Landroid/app/job/JobInfo; 11
-Lcom/android/internal/content/om/OverlayConfig; 11
-Landroid/webkit/WebViewZygote; 11
-Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringSet; 11
-Lcom/android/internal/infra/AbstractRemoteService$VultureCallback; 11
-Landroid/permission/PermissionManagerInternal; 11
-Lcom/android/server/WidgetBackupProvider; 11
-Landroid/window/WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper; 11
-Landroid/app/PropertyInvalidatedCache;.sCorkedInvalidates:Ljava/util/HashMap; 11
-Landroid/media/AudioPlaybackConfiguration$PlayerDeathMonitor; 11
-Landroid/net/wifi/nl80211/WifiNl80211Manager$ScanEventCallback; 11
-Landroid/service/notification/NotificationListenerService$RankingMap; 11
-Landroid/os/UserHandle;.sExtraUserHandleCache:Landroid/util/SparseArray; 11
-Ljava/time/DateTimeException; 11
-Ljava/lang/NumberFormatException; 11
-Ljava/security/Provider;.knownEngines:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.125:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 11
-Landroid/app/LoadedApk$ServiceDispatcher$RunConnection; 11
-Landroid/view/RoundedCorners; 11
-Landroid/os/Process;.ZYGOTE_PROCESS:Landroid/os/ZygoteProcess; 11
-Landroid/media/audiopolicy/AudioVolumeGroup; 11
-Landroid/media/AudioSystem$ErrorCallback; 11
-Landroid/app/servertransaction/ActivityResultItem; 11
-Lcom/android/internal/widget/DialogTitle; 11
-Lcom/android/internal/os/StoragedUidIoStatsReader$Callback; 11
-Landroid/view/ViewRootImpl$W; 11
-Landroid/app/ServiceStartArgs; 11
-Landroid/window/TaskAppearedInfo; 11
-Lcom/android/internal/listeners/ListenerExecutor$FailureCallback; 11
-Landroid/app/ApplicationExitInfo; 11
-Landroid/content/pm/PackageManager;.sCacheAutoCorker:Landroid/app/PropertyInvalidatedCache$AutoCorker;.mLock:Ljava/lang/Object; 11
-Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringValueMap; 11
-Landroid/content/pm/ResolveInfo; 11
-Lcom/android/internal/display/BrightnessSynchronizer; 11
-Landroid/window/IOnBackInvokedCallback$Stub$Proxy; 12
-Landroid/graphics/drawable/PictureDrawable; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.126:Ljava/lang/Byte; 13
-Landroid/view/ViewDebug$ExportedProperty; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.41:Ljava/lang/Byte; 13
-Landroid/view/inputmethod/DeleteGesture; 13
-Landroid/view/ViewDebug$IntToString; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.56:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.65:Ljava/lang/Byte; 13
-Landroid/webkit/WebViewFactory;.sProviderLock:Ljava/lang/Object; 13
-Ljava/lang/IllegalAccessError; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.51:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.52:Ljava/lang/Byte; 13
-Landroid/view/inputmethod/DeleteRangeGesture; 13
-Landroid/window/WindowContext; 13
-Ljava/util/concurrent/ConcurrentSkipListMap$Node; 13
-Landroid/view/inputmethod/SelectRangeGesture; 13
-Landroid/util/MalformedJsonException; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.131:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.120:Ljava/lang/Byte; 13
-Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 13
-Ljava/nio/file/StandardOpenOption;.TRUNCATE_EXISTING:Ljava/nio/file/StandardOpenOption; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.121:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.16:Ljava/lang/Byte; 13
-Ljava/util/concurrent/ConcurrentSkipListMap$Index; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.139:Ljava/lang/Byte; 13
-Landroid/view/ViewDebug$FlagToString; 13
-Landroid/view/inputmethod/SelectGesture; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.20:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.94:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.64:Ljava/lang/Byte; 13
-Landroid/webkit/WebViewFactoryProvider$Statics; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.95:Ljava/lang/Byte; 13
-Landroid/service/media/MediaBrowserService$ServiceBinder$1; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.7:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.23:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.46:Ljava/lang/Byte; 13
-Landroid/provider/Settings$SettingNotFoundException; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.74:Ljava/lang/Byte; 13
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.8:Ljava/lang/Byte; 13
-Landroid/widget/TextView;.TEMP_POSITION:[F 13
-Ljava/io/ByteArrayInputStream; 14
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.93:Ljava/lang/Byte; 14
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.134:Ljava/lang/Byte; 14
-Landroid/text/style/ImageSpan; 14
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.154:Ljava/lang/Byte; 15
-Landroid/view/TextureView$SurfaceTextureListener; 16
-Landroid/media/AudioManager$OnAudioFocusChangeListener; 17
-Ljava/util/Locale;.JAPAN:Ljava/util/Locale; 18
-Ljava/util/Locale;.GERMANY:Ljava/util/Locale; 19
-Ljava/util/Locale;.CANADA_FRENCH:Ljava/util/Locale; 20
-Ljava/util/Locale;.ITALY:Ljava/util/Locale; 20
-Ljava/util/Locale;.FRANCE:Ljava/util/Locale; 20
-Ljava/util/Locale;.UK:Ljava/util/Locale; 21
-Ljava/util/Locale;.CANADA:Ljava/util/Locale; 21
-Ljava/util/Locale$Cache;.LOCALECACHE:Ljava/util/Locale$Cache;.map:Ljava/util/concurrent/ConcurrentMap; 22
-Ljava/lang/IllegalStateException; 23
-Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sMessageCallbacksPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool; 24
-Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sMessageCallbacksPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool;.mPool:[Ljava/lang/Object; 24
-Landroid/media/MediaRouter$WifiDisplayStatusChangedReceiver; 25
-Landroid/media/MediaRouter$VolumeChangeReceiver; 25
-Landroid/app/AppOpsManager$OnOpActiveChangedListener; 26
-Landroid/media/PlayerBase; 27
-Landroid/content/pm/Checksum$Type; 28
-Ljava/lang/Class; 29
-Landroid/widget/MediaController$MediaPlayerControl; 30
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.135:Ljava/lang/Long; 30
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.152:Ljava/lang/Long; 30
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.215:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.206:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.137:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.203:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.213:Ljava/lang/Byte; 31
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.549:Ljava/lang/Long; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.201:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.249:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.163:Ljava/lang/Byte; 31
-Ljava/util/HashMap; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.210:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.161:Ljava/lang/Byte; 31
-Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.0:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.199:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.248:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.252:Ljava/lang/Byte; 31
-Lcom/android/ims/rcs/uce/UceDeviceState;.DEVICE_STATE_DESCRIPTION:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.3:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.159:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.217:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.200:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.240:Ljava/lang/Byte; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.198:Ljava/lang/Byte; 31
-Lcom/android/ims/rcs/uce/UceDeviceState;.DEVICE_STATE_DESCRIPTION:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.4:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 31
-Landroid/content/pm/PackageManager$OnChecksumsReadyListener; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.193:Ljava/lang/Byte; 31
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.228:Ljava/lang/Long; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.236:Ljava/lang/Byte; 31
-Landroid/telephony/ims/ImsService;.CAPABILITIES_LOG_MAP:Ljava/util/Map;.table:[Ljava/lang/Object;.2:Ljava/lang/Long; 31
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.211:Ljava/lang/Byte; 31
-Landroid/view/SurfaceView; 32
-Landroid/view/ViewStub$OnInflateListener; 33
-Landroid/graphics/drawable/DrawableInflater;.CONSTRUCTOR_MAP:Ljava/util/HashMap; 34
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.245:Ljava/lang/Byte; 35
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.232:Ljava/lang/Byte; 35
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.12:Ljava/lang/Byte; 35
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.170:Ljava/lang/Long; 35
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.183:Ljava/lang/Long; 35
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.246:Ljava/lang/Byte; 35
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.168:Ljava/lang/Long; 35
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.72:Ljava/lang/Byte; 35
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.243:Ljava/lang/Byte; 35
-Ljava/util/WeakHashMap;.NULL_KEY:Ljava/lang/Object; 35
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.235:Ljava/lang/Byte; 35
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.147:Ljava/lang/Long; 35
-Ljava/io/InterruptedIOException; 35
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.184:Ljava/lang/Long; 35
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.165:Ljava/lang/Long; 35
-Landroid/text/style/ForegroundColorSpan; 35
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.176:Ljava/lang/Long; 35
-Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.173:Ljava/lang/Long; 35
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.181:Ljava/lang/Byte; 35
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.157:Ljava/lang/Byte; 35
-Landroid/content/res/AssetManager$AssetInputStream; 35
-Landroid/graphics/drawable/TransitionDrawable; 36
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1:Ljava/lang/Boolean; 37
-Landroid/view/ViewOverlay$OverlayViewGroup; 38
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.11:Ljava/lang/Boolean; 39
-Ljava/util/Observer; 40
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.129:Ljava/lang/Byte; 41
-[Ljava/lang/Byte; 41
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.144:Ljava/lang/Byte; 41
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.164:Ljava/lang/Byte; 42
-Landroid/view/OrientationEventListener; 43
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.195:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.233:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.229:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.128:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.242:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.196:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.208:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.212:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.228:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.205:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.197:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.204:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.207:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.223:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.244:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.174:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.194:Ljava/lang/Byte; 44
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.225:Ljava/lang/Byte; 45
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.239:Ljava/lang/Byte; 45
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.238:Ljava/lang/Byte; 45
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.227:Ljava/lang/Byte; 45
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.152:Ljava/lang/Byte; 46
-Landroid/app/RemoteAction; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.168:Ljava/lang/Byte; 46
-Landroid/text/style/QuoteSpan; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.54:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.124:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.142:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.190:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.114:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.69:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.30:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.133:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.49:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.58:Ljava/lang/Byte; 46
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.143:Ljava/lang/Byte; 47
-Landroid/icu/text/RelativeDateTimeFormatter$AbsoluteUnit; 47
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.82:Ljava/lang/Byte; 47
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.140:Ljava/lang/Byte; 47
-Landroid/icu/text/RelativeDateTimeFormatter;.fallbackCache:[Landroid/icu/text/RelativeDateTimeFormatter$Style; 47
-Landroid/icu/text/RelativeDateTimeFormatter$Style; 47
-Landroid/icu/text/RelativeDateTimeFormatter;.cache:Landroid/icu/text/RelativeDateTimeFormatter$Cache;.cache:Landroid/icu/impl/CacheBase;.map:Ljava/util/concurrent/ConcurrentHashMap; 47
-Landroid/icu/text/RelativeDateTimeFormatter$RelativeUnit; 47
-Landroid/icu/text/RelativeDateTimeFormatter$Direction; 47
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.130:Ljava/lang/Byte; 47
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.43:Ljava/lang/Byte; 47
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.146:Ljava/lang/Byte; 47
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.138:Ljava/lang/Byte; 47
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.136:Ljava/lang/Byte; 48
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.0:Ljava/lang/Byte; 49
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.160:Ljava/lang/Byte; 49
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.169:Ljava/lang/Byte; 50
-Landroid/widget/Spinner; 50
-Landroid/widget/MultiAutoCompleteTextView; 50
-Ljava/util/ArrayList; 50
-Landroid/widget/CheckBox; 50
-Ljava/io/Serializable; 50
-Landroid/widget/RatingBar; 50
-Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.132:Ljava/lang/Byte; 50
-Landroid/widget/AutoCompleteTextView; 50
-Ljava/util/concurrent/ConcurrentLinkedDeque$Node; 50
-[Ljava/lang/Object; 50
-Landroid/widget/SeekBar; 51
-Ljava/lang/Void; 52
-Landroid/app/ActivityTaskManager;.sInstance:Landroid/util/Singleton; 53
-Landroid/view/ViewRootImpl$$ExternalSyntheticLambda11; 54
-Landroid/view/ViewTreeObserver$OnWindowFocusChangeListener; 55
-Landroid/view/InsetsAnimationThread; 56
-Lcom/android/internal/jank/InteractionJankMonitor$InstanceHolder; 57
-Lcom/android/internal/jank/InteractionJankMonitor; 57
-Landroid/hardware/camera2/CameraCharacteristics;.FLASH_INFO_AVAILABLE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 58
-Landroid/hardware/display/NightDisplayListener$Callback; 59
-Landroid/media/MediaRouter2Manager; 59
-Landroid/os/HandlerExecutor; 59
-Landroid/os/strictmode/LeakedClosableViolation; 60
-Lcom/android/internal/logging/MetricsLogger; 60
-Lcom/android/internal/os/PowerProfile;.sPowerItemMap:Ljava/util/HashMap; 61
-Lcom/android/internal/os/PowerProfile;.sPowerArrayMap:Ljava/util/HashMap; 61
-Lcom/android/internal/os/PowerProfile;.sModemPowerProfile:Lcom/android/internal/power/ModemPowerProfile;.mPowerConstants:Landroid/util/SparseDoubleArray;.mValues:Landroid/util/SparseLongArray; 61
-Landroid/content/IntentFilter; 62
-Landroid/telecom/TelecomManager; 63
-Ljava/lang/IllegalArgumentException; 64
-Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 65
-Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 65
-Landroid/telephony/VisualVoicemailSmsFilterSettings;.DEFAULT_ORIGINATING_NUMBERS:Ljava/util/List; 66
-Ljava/util/AbstractList; 68
-Ljava/util/AbstractCollection; 68
-Ljava/util/Collections$EmptyList; 69
-Ljava/lang/StackTraceElement; 69
-[Ljava/lang/StackTraceElement; 69
-Landroid/os/strictmode/Violation; 70
-Ljava/util/List; 71
-Ljava/lang/String; 72
-Ljava/io/ObjectInputStream; 73
-Ljava/io/ObjectStreamClass$Caches;.localDescs:Ljava/util/concurrent/ConcurrentMap; 73
-Ljava/io/ObjectStreamClass$Caches;.reflectors:Ljava/util/concurrent/ConcurrentMap; 73
-Ljava/io/ObjectOutputStream; 73
-Ljava/lang/Number; 74
-Ljava/math/BigInteger; 75
-[B 76
-Landroid/os/Handler; 77
-Landroid/view/accessibility/AccessibilityManager; 78
-Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray;.mKeys:[I 79
-Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 79
-Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray; 79
-Landroid/widget/FrameLayout; 80
-Lcom/android/internal/inputmethod/ImeTracing; 80
-Lcom/android/internal/policy/DecorView; 80
-Landroid/view/accessibility/AccessibilityNodeIdManager; 80
-Landroid/view/ViewTreeObserver; 80
-Landroid/view/ViewRootImpl; 80
-Landroid/os/SystemProperties;.sChangeCallbacks:Ljava/util/ArrayList; 80
-Landroid/transition/ChangeTransform; 80
-Landroid/window/SurfaceSyncGroup; 80
-Landroid/transition/ChangeClipBounds; 80
-Landroid/view/SurfaceControlRegistry; 80
-Landroid/transition/ChangeImageTransform; 80
-Landroid/widget/LinearLayout; 80
-Landroid/view/ViewStub; 81
-Landroid/text/TextLine;.sCached:[Landroid/text/TextLine; 82
-Landroid/text/TextUtils; 82
-Landroid/graphics/TemporaryBuffer; 82
-Landroid/content/res/ColorStateList;.sCache:Landroid/util/SparseArray; 83
-Landroid/text/Layout;.sTempRect:Landroid/graphics/Rect; 84
-Landroid/widget/ImageView; 85
-Landroid/graphics/drawable/ColorDrawable; 86
-Landroid/os/StrictMode$InstanceTracker;.sInstanceCounts:Ljava/util/HashMap; 87
-Landroid/app/ActivityClient;.INTERFACE_SINGLETON:Landroid/app/ActivityClient$ActivityClientControllerSingleton; 88
-Landroid/app/ActivityClient;.sInstance:Landroid/util/Singleton; 88
-Landroid/view/AbsSavedState$1; 89
-Landroid/app/FragmentManagerState; 90
-Landroid/window/OnBackAnimationCallback; 91
-Landroid/animation/AnimatorInflater;.sTmpTypedValue:Landroid/util/TypedValue; 92
-Landroid/graphics/drawable/RippleDrawable; 93
-Landroid/view/inputmethod/IInputMethodManagerGlobalInvoker; 94
-Landroid/app/ActivityTaskManager;.IActivityTaskManagerSingleton:Landroid/util/Singleton; 95
-Landroid/view/Choreographer; 96
-Lcom/android/internal/os/SomeArgs; 97
-Landroid/graphics/Bitmap; 98
-Landroid/view/autofill/AutofillId; 99
-Landroid/view/inputmethod/BaseInputConnection;.COMPOSING:Ljava/lang/Object; 100
-Landroid/text/Selection;.SELECTION_MEMORY:Ljava/lang/Object; 101
-Landroid/text/Selection;.SELECTION_END:Ljava/lang/Object; 101
-Landroid/text/Selection;.SELECTION_START:Ljava/lang/Object; 101
-Landroid/text/SpannableStringBuilder;.sCachedIntBuffer:[[I 102
-Landroid/text/Selection$MemoryTextWatcher; 103
-Landroid/text/SpanWatcher; 104
-Lcom/android/internal/util/ArrayUtils;.sCache:[Ljava/lang/Object; 105
-Ljava/lang/Integer;.SMALL_NEG_VALUES:[Ljava/lang/String; 106
-Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 107
-Lsun/nio/ch/SharedFileLockTable;.lockMap:Ljava/util/concurrent/ConcurrentHashMap; 108
-Lsun/nio/ch/FileChannelImpl; 108
-Landroid/database/sqlite/SQLiteDatabase$CursorFactory; 109
-Landroid/database/sqlite/SQLiteDebug$NoPreloadHolder; 110
-Landroid/database/sqlite/SQLiteCompatibilityWalFlags; 110
-Landroid/database/sqlite/SQLiteGlobal; 110
-Landroid/database/CursorWindow; 111
-Landroid/content/ContentResolver; 112
-Ljava/nio/charset/Charset; 113
-Landroid/app/ContextImpl; 114
-Ljava/util/concurrent/Executors$DefaultThreadFactory;.poolNumber:Ljava/util/concurrent/atomic/AtomicInteger; 115
-Landroid/content/pm/PackageManager;.sPackageInfoCache:Landroid/app/PropertyInvalidatedCache;.mCache:Ljava/util/LinkedHashMap; 116
-Landroid/content/pm/PackageManager;.sApplicationInfoCache:Landroid/app/PropertyInvalidatedCache;.mCache:Ljava/util/LinkedHashMap; 117
-Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.systemContext:Ljava/util/logging/LogManager$LoggerContext;.namedLoggers:Ljava/util/Hashtable;.table:[Ljava/util/Hashtable$HashtableEntry; 118
-Landroid/ddm/DdmHandleAppName; 118
-Landroid/provider/DeviceConfigInitializer; 118
-Lsun/misc/Cleaner; 118
-Ldalvik/system/CloseGuard; 118
-Landroid/graphics/Typeface; 118
-Landroid/os/BinderProxy;.sProxyMap:Landroid/os/BinderProxy$ProxyMap;.mMainIndexKeys:[[Ljava/lang/Long; 118
-Landroid/permission/PermissionManager; 118
-Landroid/media/MediaFrameworkPlatformInitializer; 118
-Ljava/util/TimeZone; 118
-Landroid/os/Environment; 118
-Landroid/compat/Compatibility; 118
-Landroid/os/ServiceManager; 118
-Landroid/content/pm/PackageManager;.sApplicationInfoCache:Landroid/app/PropertyInvalidatedCache; 118
-Ljava/util/Locale$NoImagePreloadHolder; 118
-Ljava/lang/System; 118
-Lcom/android/internal/os/RuntimeInit; 118
-Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.systemContext:Ljava/util/logging/LogManager$LoggerContext;.namedLoggers:Ljava/util/Hashtable; 118
-Ldalvik/system/VMRuntime;.THE_ONE:Ldalvik/system/VMRuntime; 118
-Landroid/view/View; 118
-Landroid/hardware/display/DisplayManagerGlobal; 118
-Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager; 118
-Landroid/telephony/TelephonyFrameworkInitializer; 118
-Landroid/se/omapi/SeFrameworkInitializer; 118
-Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap;.mHashes:[I 118
-Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder; 118
-Landroid/security/net/config/ApplicationConfig; 118
-Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map; 118
-Ljava/util/Locale; 118
-Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map;.table:[Ljava/util/WeakHashMap$Entry; 118
-Ljava/security/Provider; 118
-Ldalvik/system/ZygoteHooks; 118
-Landroid/os/Message; 118
-Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object; 118
-Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap; 118
-Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap; 118
-Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.userContext:Ljava/util/logging/LogManager$LoggerContext;.namedLoggers:Ljava/util/Hashtable; 118
-Ljava/lang/ThreadGroup;.mainThreadGroup:Ljava/lang/ThreadGroup; 118
-Ldalvik/system/RuntimeHooks; 118
-Landroid/nfc/NfcFrameworkInitializer; 118
-Landroid/os/Looper; 118
-Landroid/os/LocaleList; 118
-Ldalvik/system/SocketTagger; 118
-Landroid/icu/util/TimeZone; 118
-Landroid/util/ArraySet; 118
-Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.systemContext:Ljava/util/logging/LogManager$LoggerContext;.root:Ljava/util/logging/LogManager$LogNode; 118
-Landroid/os/BinderProxy;.sProxyMap:Landroid/os/BinderProxy$ProxyMap;.mMainIndexValues:[Ljava/util/ArrayList; 118
-Ljava/util/Random;.seedUniquifier:Ljava/util/concurrent/atomic/AtomicLong; 118
-Landroid/app/ActivityThread; 118
-Landroid/os/Binder; 118
-Ljava/lang/ThreadLocal;.nextHashCode:Ljava/util/concurrent/atomic/AtomicInteger; 119
-Landroid/os/Parcel; 120
-Landroid/system/UnixSocketAddress; 120
-Ljava/lang/ThreadGroup;.systemThreadGroup:Ljava/lang/ThreadGroup; 120
-Ljava/lang/Daemons$FinalizerDaemon;.INSTANCE:Ljava/lang/Daemons$FinalizerDaemon; 120
-Landroid/os/Parcel;.sPairedCreators:Ljava/util/HashMap; 120
-Ljava/lang/Thread; 120
-Landroid/os/Parcel;.mCreators:Ljava/util/HashMap; 120
-Ljava/lang/Daemons$FinalizerDaemon;.INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;.progressCounter:Ljava/util/concurrent/atomic/AtomicInteger; 120
-Landroid/system/StructPollfd; 120
-Ljava/lang/Daemons$HeapTaskDaemon;.INSTANCE:Ljava/lang/Daemons$HeapTaskDaemon; 120
-Landroid/system/StructTimeval; 120
-Ldalvik/system/VMRuntime;.THE_ONE:Ldalvik/system/VMRuntime;.allocationCount:Ljava/util/concurrent/atomic/AtomicInteger; 120
-Ljava/lang/Daemons$ReferenceQueueDaemon;.INSTANCE:Ljava/lang/Daemons$ReferenceQueueDaemon;.progressCounter:Ljava/util/concurrent/atomic/AtomicInteger; 120
-Landroid/os/GraphicsEnvironment;.sInstance:Landroid/os/GraphicsEnvironment; 120
-Ljava/lang/Daemons$FinalizerWatchdogDaemon;.INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon; 120
-Ljava/lang/ref/FinalizerReference; 120
-Landroid/os/Process; 120
-Ljava/lang/Daemons$ReferenceQueueDaemon;.INSTANCE:Ljava/lang/Daemons$ReferenceQueueDaemon; 120
-Lcom/android/internal/os/BinderInternal; 120
-Landroid/app/ApplicationLoaders;.gApplicationLoaders:Landroid/app/ApplicationLoaders;.mLoaders:Landroid/util/ArrayMap; 121
-Landroid/app/DexLoadReporter;.INSTANCE:Landroid/app/DexLoadReporter;.mDataDirs:Ljava/util/Set;.map:Ljava/util/HashMap; 122
-Ldalvik/system/BaseDexClassLoader; 122
-Landroid/renderscript/RenderScriptCacheDir; 122
-Landroid/graphics/Compatibility; 123
-Llibcore/io/Libcore; 123
-Landroid/provider/FontsContract; 123
-Ljava/security/Security;.version:Ljava/util/concurrent/atomic/AtomicInteger; 123
-Llibcore/net/NetworkSecurityPolicy; 123
-Lsun/security/jca/Providers; 123
-Landroid/graphics/Canvas; 123
-Landroid/os/StrictMode; 124
-Landroid/content/pm/PackageManager;.sPackageInfoCache:Landroid/app/PropertyInvalidatedCache; 125
-Lcom/android/internal/os/StatsdHiddenApiUsageLogger;.sInstance:Lcom/android/internal/os/StatsdHiddenApiUsageLogger; 126
-Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.loggerRefQueue:Ljava/lang/ref/ReferenceQueue; 127
-Landroid/view/WindowManagerGlobal; 128
-Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool; 129
-Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool;.mPool:[Ljava/lang/Object; 129
-Landroid/view/inputmethod/InputMethodManager; 130
-Landroid/media/MediaRouter; 131
-Landroid/hardware/SensorPrivacyManager; 132
-Landroid/os/storage/StorageManager; 133
-Landroid/view/contentcapture/ContentCaptureManager; 134
-Landroid/hardware/input/InputManager; 134
-Landroid/app/people/PeopleManager; 134
-Landroid/media/session/MediaSessionManager; 134
-Landroid/security/attestationverification/AttestationVerificationManager; 134
-Landroid/net/vcn/VcnManager; 134
-Landroid/os/RecoverySystem; 134
-Landroid/net/NetworkPolicyManager; 134
-Landroid/net/wifi/sharedconnectivity/app/SharedConnectivityManager; 134
-Landroid/permission/PermissionControllerManager; 134
-Landroid/app/tare/EconomyManager; 134
-Landroid/view/translation/TranslationManager; 134
-Landroid/view/textclassifier/TextClassificationManager; 134
-Landroid/view/autofill/AutofillManager; 134
-Landroid/os/SystemConfigManager; 134
-Landroid/view/LayoutInflater; 134
-Landroid/credentials/CredentialManager; 134
-Landroid/service/persistentdata/PersistentDataBlockManager; 134
-Landroid/view/textservice/TextServicesManager; 134
-Landroid/app/admin/DevicePolicyManager; 134
-Ljava/lang/StackStreamFactory; 134
-Landroid/view/WindowManager; 134
-Landroid/app/contentsuggestions/ContentSuggestionsManager; 134
-Landroid/media/tv/tunerresourcemanager/TunerResourceManager; 134
-Landroid/telephony/SubscriptionManager; 134
-Landroid/os/HardwarePropertiesManager; 134
-Landroid/media/AudioManager; 135
-Landroid/telephony/TelephonyManager; 136
-Landroid/util/ArrayMap; 137
-Landroid/app/QueuedWork; 138
-Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.0:Ljava/util/WeakHashMap$Entry; 139
-Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap; 140
-Ljava/util/concurrent/ScheduledThreadPoolExecutor;.sequencer:Ljava/util/concurrent/atomic/AtomicLong; 141
-Landroid/util/Log; 142
-Ljava/util/Collections$SynchronizedCollection; 143
-Ljava/util/Set; 143
-Ljava/util/Collections$SynchronizedSet; 143
-Ljava/util/Collection; 143
-Ljava/lang/Integer;.SMALL_NONNEG_VALUES:[Ljava/lang/String; 144
-Landroid/content/ComponentName; 145
-Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle; 146
-Landroid/os/PersistableBundle;.EMPTY:Landroid/os/PersistableBundle; 147
-Landroid/icu/impl/locale/BaseLocale;.CACHE:Landroid/icu/impl/locale/BaseLocale$Cache;._map:Ljava/util/concurrent/ConcurrentHashMap; 148
-Ljava/util/GregorianCalendar; 149
-Ljava/text/DontCareFieldPosition;.INSTANCE:Ljava/text/FieldPosition; 150
-Landroid/app/UiModeManager; 151
-Ljdk/internal/access/SharedSecrets; 152
-Landroid/icu/impl/ZoneMeta;.CANONICAL_ID_CACHE:Landroid/icu/impl/ICUCache; 153
-Landroid/icu/impl/ZoneMeta;.SYSTEM_ZONE_CACHE:Landroid/icu/impl/ZoneMeta$SystemTimeZoneCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 154
-Ljava/time/ZoneOffset;.ID_CACHE:Ljava/util/concurrent/ConcurrentMap; 155
-Ljava/time/ZoneOffset;.ID_CACHE:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node; 155
-Ljava/time/ZoneOffset;.SECONDS_CACHE:Ljava/util/concurrent/ConcurrentMap; 155
-Ljava/time/ZoneOffset;.SECONDS_CACHE:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.0:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node; 155
-Ljava/time/ZoneOffset;.SECONDS_CACHE:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node; 155
-Landroid/widget/TextView; 156
-Landroid/view/ViewGroup$ChildListForAutoFillOrContentCapture;.sPool:Landroid/util/Pools$SimplePool;.mPool:[Ljava/lang/Object; 157
-Landroid/view/ViewGroup$ChildListForAutoFillOrContentCapture;.sPool:Landroid/util/Pools$SimplePool; 157
-Landroid/view/ViewGroup; 158
-Landroid/graphics/Rect; 159
-Landroid/view/View$BaseSavedState; 160
-Landroid/widget/Button; 161
-Landroid/widget/ImageButton; 162
-Landroid/view/View$OnHoverListener; 163
-Landroid/widget/Toolbar; 164
-Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal; 165
-Landroid/app/WallpaperManager; 166
-Landroid/graphics/ColorSpace$Model;.RGB:Landroid/graphics/ColorSpace$Model; 166
-Landroid/graphics/drawable/AdaptiveIconDrawable; 167
-Landroid/animation/ValueAnimator$DurationScaleChangeListener; 168
-Landroid/widget/Toast; 168
-Landroid/app/smartspace/SmartspaceSession$OnTargetsAvailableListener; 168
-Landroid/view/CrossWindowBlurListeners; 168
-Landroid/app/servertransaction/ActivityRelaunchItem; 169
-[Ljava/util/concurrent/ForkJoinTask; 169
-Landroid/view/WindowManager$LayoutParams; 169
-Ljava/util/concurrent/ForkJoinPool$WorkQueue; 169
-Landroid/app/prediction/AppTargetEvent; 169
-Lorg/xmlpull/v1/XmlPullParserException; 169
-Landroid/app/servertransaction/ObjectPool;.sPoolMap:Ljava/util/Map; 170
-Landroid/app/servertransaction/ClientTransaction; 170
-Landroid/app/servertransaction/StopActivityItem; 170
-Landroid/system/ErrnoException; 171
-Landroid/hardware/location/ContextHubTransaction$OnCompleteListener; 172
-Landroid/app/PendingIntent$OnFinished; 172
-Ljava/lang/NullPointerException; 173
-Landroid/os/strictmode/DiskReadViolation; 174
-Lorg/apache/http/params/HttpParams; 175
-Landroid/nfc/cardemulation/CardEmulation; 176
-Ljava/io/FileDescriptor; 177
-Landroid/content/pm/PackageManager$OnPermissionsChangedListener; 178
-Landroid/security/keystore2/KeyStoreCryptoOperationUtils; 179
-Landroid/app/ActivityTaskManager; 180
-Landroid/util/EventLog; 181
-Ljava/net/URLConnection; 181
-Ljava/net/SocketException; 181
-Ljava/lang/reflect/InvocationTargetException; 181
-Ljava/lang/Enum; 182
-Landroid/widget/AbsListView$SelectionBoundsAdjuster; 183
-Ljava/lang/ClassNotFoundException; 183
-Landroid/content/SyncStatusObserver; 184
-Landroid/content/AsyncTaskLoader$LoadTask; 185
-Landroid/app/LoaderManager$LoaderCallbacks; 185
-Landroid/webkit/CookieSyncManager; 186
-Landroid/webkit/WebViewProvider$ViewDelegate; 187
-Landroid/webkit/WebView; 187
-Landroid/webkit/WebViewProvider$ScrollDelegate; 187
-Landroid/webkit/WebViewProvider; 187
-Landroid/webkit/WebViewFactory;.sTimestamps:Landroid/webkit/WebViewFactory$StartupTimestamps; 188
-Landroid/webkit/WebViewFactoryProvider; 189
-Landroid/webkit/WebViewFactory; 190
-Landroid/os/PowerManager$OnThermalStatusChangedListener; 191
-Landroid/os/Bundle; 192
-Landroid/widget/ProgressBar; 193
-Landroid/graphics/Bitmap$Config;.ALPHA_8:Landroid/graphics/Bitmap$Config; 194
-Landroid/graphics/Bitmap$Config;.RGB_565:Landroid/graphics/Bitmap$Config; 194
-Landroid/graphics/Bitmap$Config;.RGBA_1010102:Landroid/graphics/Bitmap$Config; 194
-Landroid/renderscript/Allocation;.mBitmapOptions:Landroid/graphics/BitmapFactory$Options;.inPreferredConfig:Landroid/graphics/Bitmap$Config; 194
-Landroid/graphics/Bitmap$Config;.RGBA_F16:Landroid/graphics/Bitmap$Config; 194
-Landroid/graphics/Bitmap$Config;.ARGB_4444:Landroid/graphics/Bitmap$Config; 194
-Landroid/graphics/Bitmap$Config;.HARDWARE:Landroid/graphics/Bitmap$Config; 194
-Landroid/graphics/drawable/StateListDrawable; 195
-Landroid/view/PointerIcon;.gSystemIconsByDisplay:Landroid/util/SparseArray; 196
-Landroid/view/PointerIcon; 196
-Ljavax/net/ssl/SSLServerSocketFactory; 197
-Ljavax/net/ssl/SSLSocketFactory; 198
-Ljavax/net/ssl/HttpsURLConnection$NoPreloadHolder; 198
-Ljavax/net/ssl/SSLSessionContext; 199
-Lcom/android/org/bouncycastle/crypto/CryptoServicesRegistrar; 200
-Lsun/security/x509/PKIXExtensions;.KeyUsage_Id:Lsun/security/util/ObjectIdentifier; 201
-Lsun/security/x509/PKIXExtensions;.PolicyConstraints_Id:Lsun/security/util/ObjectIdentifier; 201
-Ljava/security/cert/PKIXRevocationChecker$Option;.ONLY_END_ENTITY:Ljava/security/cert/PKIXRevocationChecker$Option; 201
-Lsun/security/x509/PKIXExtensions;.ExtendedKeyUsage_Id:Lsun/security/util/ObjectIdentifier; 201
-Lsun/security/provider/X509Factory;.certCache:Lsun/security/util/Cache; 201
-Lsun/security/x509/PKIXExtensions;.CertificatePolicies_Id:Lsun/security/util/ObjectIdentifier; 201
-Lsun/security/x509/PKIXExtensions;.NameConstraints_Id:Lsun/security/util/ObjectIdentifier; 201
-Lsun/security/x509/PKIXExtensions;.AuthorityKey_Id:Lsun/security/util/ObjectIdentifier; 201
-Lsun/security/provider/X509Factory;.certCache:Lsun/security/util/Cache;.cacheMap:Ljava/util/Map; 201
-Ljava/security/cert/PKIXRevocationChecker$Option;.NO_FALLBACK:Ljava/security/cert/PKIXRevocationChecker$Option; 201
-Lsun/security/x509/PKIXExtensions;.SubjectAlternativeName_Id:Lsun/security/util/ObjectIdentifier; 201
-Lsun/security/x509/PKIXExtensions;.PolicyMappings_Id:Lsun/security/util/ObjectIdentifier; 202
-Lsun/security/x509/PKIXExtensions;.InhibitAnyPolicy_Id:Lsun/security/util/ObjectIdentifier; 202
-Lsun/security/x509/PKIXExtensions;.BasicConstraints_Id:Lsun/security/util/ObjectIdentifier; 202
-Ljava/security/Security;.spiMap:Ljava/util/Map; 203
-Lsun/security/x509/X500Name;.commonName_oid:Lsun/security/util/ObjectIdentifier; 204
-Lsun/security/x509/X500Name;.countryName_oid:Lsun/security/util/ObjectIdentifier; 204
-Lsun/security/x509/X500Name;.orgName_oid:Lsun/security/util/ObjectIdentifier; 204
-Ljava/nio/charset/Charset;.cache2:Ljava/util/HashMap; 205
-Ljava/net/URL;.handlers:Ljava/util/Hashtable;.table:[Ljava/util/Hashtable$HashtableEntry; 206
-Ljava/net/URL;.handlers:Ljava/util/Hashtable; 206
-Ljava/net/Inet6AddressImpl;.addressCache:Ljava/net/AddressCache;.cache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap; 207
-Ljava/net/Proxy$Type;.DIRECT:Ljava/net/Proxy$Type; 208
-Ljava/net/ProxySelector;.theProxySelector:Ljava/net/ProxySelector; 208
-Lcom/android/okhttp/okio/SegmentPool; 209
-Lcom/android/okhttp/internal/http/AuthenticatorAdapter;.INSTANCE:Lcom/android/okhttp/Authenticator; 209
-Lcom/android/okhttp/HttpsHandler;.HTTP_1_1_ONLY:Ljava/util/List;.element:Ljava/lang/Object; 209
-Lcom/android/okhttp/ConfigAwareConnectionPool;.instance:Lcom/android/okhttp/ConfigAwareConnectionPool;.networkEventDispatcher:Llibcore/net/event/NetworkEventDispatcher;.listeners:Ljava/util/List; 210
-Lcom/android/okhttp/Dns;.SYSTEM:Lcom/android/okhttp/Dns; 210
-Lcom/android/okhttp/ConfigAwareConnectionPool;.instance:Lcom/android/okhttp/ConfigAwareConnectionPool; 210
-Lcom/android/okhttp/okio/AsyncTimeout; 211
-Ljava/lang/IllegalAccessException; 212
-Ljavax/net/ssl/SSLContext; 213
-Ljavax/net/ssl/HttpsURLConnection; 213
-Ljava/security/Security;.props:Ljava/util/Properties;.map:Ljava/util/concurrent/ConcurrentHashMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.12:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node; 214
-Ljava/security/Security;.props:Ljava/util/Properties;.map:Ljava/util/concurrent/ConcurrentHashMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.30:Ljava/util/concurrent/ConcurrentHashMap$Node; 214
-Landroid/database/sqlite/SQLiteTransactionListener; 215
-Landroid/accounts/OnAccountsUpdateListener; 216
-Landroid/accounts/AccountManager$20; 217
-Lsun/nio/ch/FileChannelImpl$Unmapper; 218
-Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map;.queue:Ljava/lang/ref/ReferenceQueue; 219
-Landroid/text/method/SingleLineTransformationMethod; 220
-Landroid/widget/RelativeLayout; 221
-Landroid/graphics/drawable/BitmapDrawable; 222
-Landroid/graphics/drawable/GradientDrawable; 223
-Landroid/animation/PropertyValuesHolder;.sGetterPropertyMap:Ljava/util/HashMap; 224
-Landroid/animation/PropertyValuesHolder$FloatPropertyValuesHolder;.sJNISetterPropertyMap:Ljava/util/HashMap; 225
-Landroid/graphics/drawable/Drawable;.DEFAULT_TINT_MODE:Landroid/graphics/PorterDuff$Mode; 226
-Landroid/text/StaticLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool; 227
-Landroid/text/StaticLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 227
-Ljava/util/concurrent/ThreadLocalRandom; 228
-Landroid/widget/Space; 229
-Landroid/widget/ScrollView; 230
-Landroid/text/style/LineHeightSpan; 231
-Landroid/text/style/TabStopSpan; 232
-Landroid/text/style/ReplacementSpan; 233
-Landroid/text/style/MetricAffectingSpan; 233
-Landroid/text/style/LeadingMarginSpan; 233
-Landroid/text/style/LineBackgroundSpan; 234
-Landroid/text/style/CharacterStyle; 235
-Landroid/text/style/SuggestionSpan; 236
-Landroid/widget/TextView$ChangeWatcher; 237
-Landroid/text/DynamicLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 238
-Landroid/text/DynamicLayout; 238
-Landroid/text/DynamicLayout$ChangeWatcher; 238
-Landroid/text/style/WrapTogetherSpan; 238
-Landroid/text/DynamicLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool; 238
-Landroid/text/method/LinkMovementMethod; 239
-Landroid/text/style/ClickableSpan; 240
-Ljava/util/logging/LogRecord;.globalSequenceNumber:Ljava/util/concurrent/atomic/AtomicLong; 241
-Ljava/lang/Runtime;.currentRuntime:Ljava/lang/Runtime; 242
-Landroid/content/pm/LauncherActivityInfo; 243
-Landroid/database/sqlite/SQLiteMisuseException; 243
-Landroid/speech/tts/TextToSpeech$Connection$SetupConnectionAsyncTask; 243
-Landroid/database/sqlite/SQLiteCantOpenDatabaseException; 243
-Landroid/database/sqlite/SQLiteDatabaseCorruptException; 243
-Landroid/database/sqlite/SQLiteDatabaseLockedException; 243
-Ljava/util/Map$Entry; 243
-Ljava/util/zip/ZipException; 243
-Landroid/database/sqlite/SQLiteAccessPermException; 243
-Landroid/speech/tts/TextToSpeech$OnInitListener; 243
-Landroid/app/Notification$MessagingStyle; 244
-Landroid/text/TextUtils$TruncateAt; 245
-Landroid/app/smartspace/SmartspaceTarget; 246
-Landroid/app/prediction/AppTarget; 246
-Landroid/app/smartspace/uitemplatedata/BaseTemplateData; 246
-Landroid/location/LocationManager;.sLocationListeners:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry; 247
-Landroid/location/LocationManager;.sLocationListeners:Ljava/util/WeakHashMap; 247
-Landroid/service/notification/ConditionProviderService; 248
-Landroid/os/WorkSource; 249
-Landroid/security/keystore2/AndroidKeyStoreProvider; 249
-Ljava/net/Socket; 249
-Lcom/android/internal/listeners/ListenerTransport; 249
-Landroid/os/ParcelUuid; 250
-Landroid/telephony/emergency/EmergencyNumber; 251
-Lcom/android/internal/telephony/uicc/UiccProfile$4; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$IncomingSms; 251
-Lcom/android/internal/telephony/SmsStorageMonitor$1; 251
-Lcom/android/internal/telephony/TelephonyDevController; 251
-Lcom/android/internal/telephony/uicc/UiccController; 251
-Lcom/android/internal/telephony/emergency/EmergencyNumberTracker$1; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$DataCallSession; 251
-Lcom/android/internal/telephony/TelephonyDevController;.mSims:Ljava/util/ArrayList; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsAcsProvisioningStats; 251
-Ljava/lang/UnsupportedOperationException; 251
-Landroid/database/CursorToBulkCursorAdaptor; 251
-Lcom/android/internal/telephony/satellite/PointingAppController; 251
-Landroid/telephony/ModemActivityInfo; 251
-Lcom/android/internal/telephony/imsphone/ImsPhone; 251
-Lcom/android/internal/telephony/ServiceStateTracker; 251
-Lcom/android/internal/telephony/IccSmsInterfaceManager; 251
-Lcom/android/internal/telephony/util/NotificationChannelController$1; 251
-Lcom/android/internal/telephony/RilWakelockInfo; 251
-Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler$GsmCbTestBroadcastReceiver; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportFeatureTagStats; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$UceEventStats; 251
-Lcom/android/internal/telephony/euicc/EuiccConnector$BindingState; 251
-Lcom/android/internal/telephony/ims/ImsResolver$3; 251
-Landroid/net/NetworkPolicyManager$SubscriptionCallbackProxy; 251
-Lcom/android/internal/telephony/TelephonyDevController;.mModems:Ljava/util/ArrayList; 251
-Landroid/telephony/ims/aidl/IImsServiceController$Stub$Proxy; 251
-Lcom/android/internal/telephony/CarrierPrivilegesTracker$1; 251
-Lcom/android/internal/telephony/CommandException; 251
-Lcom/android/ims/FeatureConnector$1; 251
-Lcom/android/internal/telephony/IWapPushManager; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SipDelegateStats; 251
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mTtyModeReceivedRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251
-Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mMmiCompleteRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251
-Lcom/android/i18n/timezone/TelephonyLookup; 251
-Landroid/telephony/BarringInfo$BarringServiceInfo; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequests; 251
-Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState$14; 251
-Lcom/android/internal/telephony/SmsBroadcastUndelivered; 251
-Lcom/android/internal/telephony/LocaleTracker; 251
-Lcom/android/internal/telephony/PhoneSubInfoController; 251
-Lcom/android/internal/telephony/CarrierKeyDownloadManager$1; 251
-Lcom/android/internal/telephony/GsmCdmaCallTracker$1; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.339:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/ServiceStateTracker$1; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.353:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/euicc/EuiccCardController$SimSlotStatusChangedBroadcastReceiver; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$PresenceNotifyEvent; 251
-Lcom/android/internal/telephony/SimActivationTracker$1; 251
-Landroid/telephony/ModemInfo; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1393:[Ljava/lang/String; 251
-Landroid/telephony/CellSignalStrengthWcdma; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteProvision; 251
-Lcom/android/internal/telephony/PhoneConfigurationManager; 251
-Lcom/android/internal/telephony/SmsApplication$SmsPackageMonitor; 251
-Landroid/telephony/TelephonyRegistryManager$3; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallSession; 251
-Landroid/os/Handler$MessengerImpl; 251
-Lcom/android/internal/telephony/LocaleTracker$1; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularDataServiceSwitch; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mInCallVoicePrivacyOffRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSosMessageRecommender; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationServiceDescStats; 251
-Lcom/android/internal/telephony/uicc/UiccPkcs15$Pkcs15Selector; 251
-Lcom/android/internal/telephony/CarrierResolver$2; 251
-Lcom/android/internal/telephony/CarrierActionAgent$1; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mPhones:Ljava/util/ArrayList; 251
-Lcom/android/internal/telephony/SmsController; 251
-Lcom/android/internal/telephony/uicc/euicc/EuiccCardException; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationTermination; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1125:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/NetworkTypeController$1; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.803:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/uicc/asn1/TagNotFoundException; 251
-Lcom/android/internal/telephony/CarrierServiceBindHelper$1; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularServiceState; 251
-Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager; 251
-Lcom/android/internal/telephony/InboundSmsHandler$NewMessageNotificationActionReceiver; 251
-Lcom/android/internal/telephony/CarrierActionAgent; 251
-Lcom/android/i18n/timezone/TimeZoneFinder; 251
-Lcom/android/internal/telephony/RILRequest; 251
-Lcom/android/internal/telephony/RIL;.sRilTimeHistograms:Landroid/util/SparseArray; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.33:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/MccTable; 251
-Lcom/android/internal/telephony/uicc/UiccProfile$2; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierIdMismatch; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1235:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/uicc/UiccCarrierPrivilegeRules; 251
-Landroid/telephony/CellSignalStrengthTdscdma; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerListenerEvent; 251
-Lcom/android/internal/telephony/SmsDispatchersController; 251
-Landroid/timezone/TelephonyLookup; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteOutgoingDatagram; 251
-Lcom/android/internal/telephony/SMSDispatcher$1; 251
-Lcom/android/internal/telephony/AppSmsManager; 251
-Landroid/timezone/TimeZoneFinder; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mBackgroundCalls:Ljava/util/ArrayList; 251
-Lcom/android/ims/rcs/uce/eab/EabProvider; 251
-Lcom/android/internal/telephony/uicc/PinStorage$1; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerEvent; 251
-Landroid/telephony/CellSignalStrengthLte; 251
-Landroid/telephony/ims/ProvisioningManager$Callback$CallbackBinder; 251
-Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray;.mKeys:[I 251
-Landroid/telephony/CellSignalStrengthNr; 251
-Lcom/android/internal/telephony/SomeArgs; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mInCallVoicePrivacyOnRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251
-Lcom/android/internal/telephony/StateMachine$SmHandler; 251
-Lcom/android/internal/telephony/PackageChangeReceiver; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingShortCodeSms; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequestsV2; 251
-Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall; 251
-Lcom/android/internal/telephony/euicc/EuiccConnector$AvailableState; 251
-Lcom/android/ims/internal/IImsServiceFeatureCallback$Stub$Proxy; 251
-Landroid/telephony/data/ApnSetting;.APN_TYPE_INT_MAP:Ljava/util/Map; 251
-Lcom/android/internal/telephony/RadioInterfaceCapabilityController; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationStats; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallRatUsage; 251
-Lcom/android/internal/telephony/metrics/TelephonyMetrics; 251
-Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall; 251
-Lcom/android/internal/telephony/NetworkRegistrationManager$NetworkRegStateCallback; 251
-Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler; 251
-Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState; 251
-Lcom/android/internal/telephony/RadioConfig; 251
-Lcom/android/internal/telephony/euicc/EuiccConnector$DisconnectedState; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSession; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mDisplayInfoRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251
-Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo; 251
-Lcom/android/internal/telephony/GsmCdmaPhone; 251
-Lcom/android/internal/telephony/TelephonyTester$1; 251
-Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler$CdmaScpTestBroadcastReceiver; 251
-Lcom/android/internal/telephony/NetworkTypeController$DefaultState; 251
-Landroid/net/TelephonyNetworkSpecifier; 251
-Lcom/android/internal/telephony/NitzStateMachine; 251
-Landroid/app/timezonedetector/TimeZoneDetector; 251
-Lcom/android/internal/telephony/IntentBroadcaster$1; 251
-Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher; 251
-Lcom/android/internal/telephony/ims/ImsResolver$1; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingSms; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$UnmeteredNetworks; 251
-Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader;.packages:Ljava/util/Map;.m:Ljava/util/Map; 251
-Lcom/android/internal/telephony/euicc/EuiccController; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mForegroundCalls:Ljava/util/ArrayList; 251
-Lcom/android/internal/telephony/satellite/SatelliteModemInterface; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.531:[Ljava/lang/String; 251
-Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray; 251
-Lcom/android/internal/telephony/euicc/EuiccConnector$1; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.467:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SipMessageResponse; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportSession; 251
-Lcom/android/internal/telephony/euicc/EuiccConnector$UnavailableState; 251
-Lcom/android/internal/telephony/DeviceStateMonitor$3; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mSignalInfoRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251
-Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 251
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager; 251
-Lcom/android/internal/telephony/DisplayInfoController; 251
-Lcom/android/internal/telephony/ims/ImsResolver$2; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1377:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyServiceState$NetworkRegistrationInfo; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteController; 251
-Landroid/telephony/ims/RegistrationManager$RegistrationCallback$RegistrationBinder; 251
-Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$EmergencyNumbersInfo; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$GbaEvent; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.23:[Ljava/lang/String; 251
-Landroid/telephony/CellSignalStrengthCdma; 251
-Landroid/telephony/TelephonyLocalConnection; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteIncomingDatagram; 251
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker$2; 251
-Lcom/android/internal/telephony/ims/ImsResolver; 251
-Lcom/android/internal/telephony/SmsStorageMonitor; 251
-Lcom/android/internal/telephony/uicc/UiccProfile; 251
-Landroid/telephony/ims/ImsMmTelManager$CapabilityCallback$CapabilityBinder; 251
-Lcom/android/internal/telephony/euicc/EuiccCardController; 251
-Lcom/android/internal/telephony/SmsBroadcastUndelivered$1; 251
-Lcom/android/internal/telephony/GsmCdmaCallTracker; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsClientProvisioningStats; 251
-Lcom/android/internal/telephony/cat/CatService; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.761:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/SmsApplication; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mDisconnectRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251
-Lcom/android/internal/telephony/PhoneFactory; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mHandlerMap:Ljava/util/HashMap; 251
-Landroid/os/AsyncResult; 251
-Lcom/android/internal/telephony/ProxyController; 251
-Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler$CdmaCbTestBroadcastReceiver; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.453:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/MultiSimSettingController; 251
-Ljava/io/BufferedReader; 251
-Landroid/telephony/CellSignalStrengthGsm; 251
-Lcom/android/internal/telephony/SimActivationTracker; 251
-Lcom/android/internal/telephony/CellBroadcastServiceManager; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mRingingCalls:Ljava/util/ArrayList; 251
-Lcom/android/internal/telephony/IntentBroadcaster; 251
-Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationFeatureTagStats; 251
-Lcom/android/internal/telephony/euicc/EuiccConnector$EuiccPackageMonitor; 251
-Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mSuppServiceFailedRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251
-Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader;.packages:Ljava/util/Map;.m:Ljava/util/Map;.table:[Ljava/util/HashMap$Node; 251
-Lcom/android/internal/telephony/CarrierServiceBindHelper$CarrierServicePackageMonitor; 251
-Lcom/android/internal/telephony/TelephonyComponentFactory; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.637:[Ljava/lang/String; 251
-Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo; 251
-Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.651:[Ljava/lang/String; 251
-Lcom/android/internal/telephony/SmsUsageMonitor; 251
-Lcom/android/internal/telephony/CommandException$Error;.INVALID_SIM_STATE:Lcom/android/internal/telephony/CommandException$Error; 251
-Landroid/hardware/display/DisplayManagerGlobal$DisplayManagerCallback; 252
-Landroid/app/ActivityThread$ApplicationThread; 252
-Landroid/app/ActivityManager$MyUidObserver; 253
-Landroid/media/browse/MediaBrowser$ServiceCallbacks; 253
-Landroid/media/session/MediaController$CallbackStub; 253
-Landroid/media/session/MediaSessionManager$OnMediaKeyEventSessionChangedListener; 253
-Landroid/app/PendingIntent$CancelListener; 253
-Landroid/media/AudioManager$2; 253
-Landroid/database/ContentObserver$Transport; 253
-Landroid/media/session/MediaSessionManager$SessionsChangedWrapper$1; 253
-Landroid/content/ContentProvider$PipeDataWriter; 254
-Landroid/security/net/config/UserCertificateSource$NoPreloadHolder; 255
-Landroid/view/Window$Callback; 256
-Landroid/transition/TransitionManager;.sDefaultTransition:Landroid/transition/Transition;.mTransitions:Ljava/util/ArrayList;.elementData:[Ljava/lang/Object;.1:Landroid/transition/ChangeBounds;.mCurrentAnimators:Ljava/util/ArrayList; 256
-Landroid/transition/TransitionManager;.sDefaultTransition:Landroid/transition/Transition;.mTransitions:Ljava/util/ArrayList;.elementData:[Ljava/lang/Object;.2:Landroid/transition/Fade;.mCurrentAnimators:Ljava/util/ArrayList; 256
-Landroid/transition/TransitionManager;.sPendingTransitions:Ljava/util/ArrayList; 256
-Landroid/transition/TransitionManager;.sDefaultTransition:Landroid/transition/Transition;.mTransitions:Ljava/util/ArrayList;.elementData:[Ljava/lang/Object;.0:Landroid/transition/Fade;.mCurrentAnimators:Ljava/util/ArrayList; 256
-Landroid/view/AttachedSurfaceControl$OnBufferTransformHintChangedListener; 257
-Landroid/webkit/ValueCallback; 258
-Landroid/webkit/WebResourceRequest; 258
-Landroid/webkit/WebChromeClient$CustomViewCallback; 258
-Landroid/hardware/camera2/CameraCharacteristics;.INFO_SUPPORTED_HARDWARE_LEVEL:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 258
-Landroid/accounts/Account; 258
-Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 258
-Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/params/StreamConfigurationDuration; 259
-Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/params/HighSpeedVideoConfiguration; 259
-Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-I 259
-Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_CAPABILITIES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259
-Landroid/hardware/camera2/params/StreamConfiguration; 259
-Z 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PIXEL_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.LENS_FOCAL_LENGTH:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_APERTURES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.DISTORTION_CORRECTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.EXTENSION_STRENGTH:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-J 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PHYSICAL_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.TONEMAP_PRESET_CURVE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-B 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_TRIGGER:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_EXPOSURE_COMPENSATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-[D 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_SESSION_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.SENSOR_EXPOSURE_TIME:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_CALIBRATION_TRANSFORM2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_TRANSFORM:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/content/res/Resources$Theme; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-F 260
-Landroid/hardware/camera2/CaptureRequest;.LENS_FILTER_DENSITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.LENS_OPTICAL_STABILIZATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.NOISE_REDUCTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.SENSOR_FRAME_DURATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_EXTENDED_SCENE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.STATISTICS_OIS_DATA_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.SENSOR_TEST_PATTERN_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.HOT_PIXEL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_ANTIBANDING_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.STATISTICS_LENS_SHADING_MAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.SCALER_CROP_REGION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.LENS_FOCUS_DISTANCE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.TONEMAP_GAMMA:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_REFERENCE_ILLUMINANT2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_REFERENCE_ILLUMINANT1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-[F 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_ZOOM_RATIO:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_ABERRATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.TONEMAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.SCALER_ROTATE_AND_CROP:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_GAINS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_COLOR_TRANSFORM1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.SENSOR_SENSITIVITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_FOCAL_LENGTHS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_OPTICAL_BLACK_REGIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.JPEG_QUALITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.FLASH_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_POST_RAW_SENSITIVITY_BOOST:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_WHITE_LEVEL:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_SETTINGS_OVERRIDE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-[Landroid/hardware/camera2/params/MeteringRectangle; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_EXPOSURE_TIME_RANGE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Ljava/lang/Float; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_ENABLE_ZSL:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.INFO_DEVICE_STATE_ORIENTATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_CALIBRATION_TRANSFORM1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.EDGE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_CAPTURE_INTENT:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_ORIENTATION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.JPEG_ORIENTATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_COLOR_TRANSFORM2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-[J 260
-Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Ljava/util/concurrent/Phaser; 260
-Landroid/hardware/camera2/CaptureRequest;.BLACK_LEVEL_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_SCENE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.JPEG_THUMBNAIL_SIZE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.SHADING_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.STATISTICS_FACE_DETECT_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.STATISTICS_HOT_PIXEL_MAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AUTOFRAMING:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_TARGET_FPS_RANGE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.SENSOR_TEST_PATTERN_DATA:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_PRECAPTURE_TRIGGER:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.FLASH_STRENGTH_LEVEL:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_VIDEO_STABILIZATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Ljava/lang/Boolean; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_EFFECT_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.LENS_APERTURE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.JPEG_THUMBNAIL_QUALITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-Ljava/lang/Long; 260
-Landroid/hardware/camera2/CaptureRequest;.SENSOR_PIXEL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260
-[Ljava/lang/String; 261
-[Z 262
-Ljava/lang/Class$Caches;.genericInterfaces:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap; 263
-Ljava/util/Map; 264
-Ljava/nio/Bits; 265
-Ljava/nio/DirectByteBuffer; 266
-Ljava/io/File; 267
-Ljava/nio/ByteBuffer; 268
-Ljava/io/InputStream; 269
-Landroid/os/ParcelFileDescriptor; 270
-Landroid/os/BinderProxy;.sProxyMap:Landroid/os/BinderProxy$ProxyMap; 271
-Landroid/app/PendingIntent; 272
-Landroid/content/Intent; 273
-Landroid/net/Uri$HierarchicalUri; 274
-Landroid/net/Uri$StringUri; 275
-Landroid/net/Uri$PathPart;.EMPTY:Landroid/net/Uri$PathPart; 276
-Lcom/android/internal/telephony/MccTable;.FALLBACKS:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.6:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 277
-Landroid/icu/text/DecimalFormatSymbols;.cachedLocaleData:Landroid/icu/impl/CacheBase;.map:Ljava/util/concurrent/ConcurrentHashMap; 278
-Llibcore/icu/DecimalFormatData;.CACHE:Ljava/util/concurrent/ConcurrentHashMap; 279
-Landroid/icu/impl/CurrencyData;.provider:Landroid/icu/impl/CurrencyData$CurrencyDisplayInfoProvider; 280
-Lcom/android/internal/infra/AndroidFuture; 281
-Lcom/android/internal/util/LatencyTracker$Action; 282
-Landroid/app/AppOpsManager$Mode; 283
-Landroid/view/accessibility/AccessibilityManager$AccessibilityServicesStateChangeListener; 284
-Landroid/annotation/IdRes; 285
-Landroid/content/pm/PackageItemInfo; 286
-Ljava/util/Random; 287
-Landroid/widget/RadioButton; 288
-Lcom/android/internal/policy/PhoneWindow$PanelFeatureState$SavedState; 289
-Landroid/graphics/Insets; 290
-Landroid/view/View;.sNextGeneratedId:Ljava/util/concurrent/atomic/AtomicInteger; 291
-Landroid/graphics/drawable/LayerDrawable; 292
-Landroid/animation/LayoutTransition; 293
-Llibcore/reflect/AnnotationFactory;.cache:Ljava/util/Map; 294
-Llibcore/reflect/AnnotationFactory;.cache:Ljava/util/Map;.table:[Ljava/util/WeakHashMap$Entry; 294
-Ljava/lang/reflect/Proxy;.proxyClassCache:Ljava/lang/reflect/WeakCache;.reverseMap:Ljava/util/concurrent/ConcurrentMap; 295
-Ljava/lang/reflect/Proxy$ProxyClassFactory;.nextUniqueNumber:Ljava/util/concurrent/atomic/AtomicLong; 295
-Ljava/lang/reflect/Proxy;.proxyClassCache:Ljava/lang/reflect/WeakCache;.map:Ljava/util/concurrent/ConcurrentMap; 296
-Ljava/lang/Object; 297
-Ljava/lang/invoke/MethodType;.internTable:Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet;.map:Ljava/util/concurrent/ConcurrentMap; 298
-Ljava/nio/channels/SocketChannel;.dexCache:Ljava/lang/Object; 298
-Ljava/lang/invoke/MethodType;.objectOnlyTypes:[Ljava/lang/invoke/MethodType; 299
-Ljava/util/concurrent/ForkJoinTask; 300
-Ljava/util/concurrent/CompletableFuture; 301
-Landroid/app/Notification$BigTextStyle; 302
-Landroid/content/pm/ApplicationInfo; 303
-Ljava/security/Signature;.signatureInfo:Ljava/util/Map;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.13:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node; 304
-Lsun/security/x509/X500Name;.stateName_oid:Lsun/security/util/ObjectIdentifier; 305
-Lsun/security/x509/X500Name;.localityName_oid:Lsun/security/util/ObjectIdentifier; 306
-Lsun/security/x509/X500Name;.orgUnitName_oid:Lsun/security/util/ObjectIdentifier; 306
-Ljava/util/UUID; 307
-Landroid/app/slice/Slice; 308
-Ljava/util/Locale;.FRENCH:Ljava/util/Locale; 308
-Landroid/os/NullVibrator; 308
-Ldalvik/system/CloseGuard;.MESSAGE:Ljava/lang/String; 308
-Lsun/util/locale/BaseLocale$Cache;.CACHE:Lsun/util/locale/BaseLocale$Cache;.map:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.22:Ljava/util/concurrent/ConcurrentHashMap$Node;.val:Ljava/lang/Object;.referent:Ljava/lang/Object; 308
-Ljava/util/Locale$Cache;.LOCALECACHE:Ljava/util/Locale$Cache;.map:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.24:Ljava/util/concurrent/ConcurrentHashMap$Node;.val:Ljava/lang/Object;.referent:Ljava/lang/Object; 308
-Landroid/app/Activity$$ExternalSyntheticLambda0; 308
-Landroid/icu/impl/locale/BaseLocale;.CACHE:Landroid/icu/impl/locale/BaseLocale$Cache;._map:Ljava/util/concurrent/ConcurrentHashMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.0:Ljava/util/concurrent/ConcurrentHashMap$Node; 308
-Ljava/util/Locale;.ITALIAN:Ljava/util/Locale; 308
-Landroid/media/MediaRouter2Manager$Callback; 308
-Lsun/util/locale/BaseLocale$Cache;.CACHE:Lsun/util/locale/BaseLocale$Cache;.map:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.29:Ljava/util/concurrent/ConcurrentHashMap$Node;.val:Ljava/lang/Object;.referent:Ljava/lang/Object; 308
-Ljava/util/Locale;.GERMAN:Ljava/util/Locale; 309
-Landroid/icu/impl/StandardPlural; 310
-Landroid/icu/impl/number/range/StandardPluralRanges; 311
-Landroid/icu/impl/PluralRulesLoader;.loader:Landroid/icu/impl/PluralRulesLoader; 311
-Landroid/icu/impl/PluralRulesLoader;.loader:Landroid/icu/impl/PluralRulesLoader;.pluralRulesCache:Ljava/util/Map; 311
-Landroid/icu/text/PluralRules$Operand; 311
-Landroid/icu/util/Calendar;.PATTERN_CACHE:Landroid/icu/impl/ICUCache; 312
-Landroid/icu/impl/DateNumberFormat;.CACHE:Landroid/icu/impl/SimpleCache; 313
-Landroid/text/format/DateFormat; 314
-Landroid/view/View$OnDragListener; 315
-Landroid/hardware/input/InputManager$InputDeviceListener; 316
-Landroid/hardware/input/InputManagerGlobal; 317
-Landroid/hardware/SystemSensorManager; 318
-Lcom/android/internal/os/BackgroundThread; 319
-Ljava/lang/Throwable; 320
-Landroid/app/NotificationManager; 321
-Landroid/app/NotificationChannel; 322
-Landroid/content/SharedPreferences$OnSharedPreferenceChangeListener; 323
-Landroid/content/pm/VersionedPackage; 324
-Landroid/app/AppOpsManager; 325
-Ldalvik/system/ZipPathValidator; 326
-Landroid/content/pm/PackageManager;.sPackageInfoCache:Landroid/app/PropertyInvalidatedCache;.mSkips:[J 327
-Landroid/content/pm/PackageManager;.sApplicationInfoCache:Landroid/app/PropertyInvalidatedCache;.mSkips:[J 328
-Lsun/util/locale/BaseLocale$Cache;.CACHE:Lsun/util/locale/BaseLocale$Cache;.map:Ljava/util/concurrent/ConcurrentMap; 329
-Landroid/content/Context; 330
-Ljava/util/concurrent/Executor; 331
-Ljava/util/concurrent/ScheduledExecutorService; 332
-Ljava/util/concurrent/ExecutorService; 332
-Landroid/view/Window$OnFrameMetricsAvailableListener; 333
-Ljava/lang/annotation/Annotation; 334
-Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 335
-Ljava/util/concurrent/CancellationException; 336
-Ljava/lang/NoSuchMethodException; 337
-Landroid/os/strictmode/CustomViolation; 338
-Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.3:Ljava/util/WeakHashMap$Entry; 339
-Lcom/android/internal/policy/PhoneWindow; 340
-Landroid/view/autofill/AutofillValue; 340
-Landroid/widget/TextView$SavedState; 341
-Landroid/text/method/MetaKeyKeyListener;.SYM:Ljava/lang/Object; 342
-Landroid/text/method/MetaKeyKeyListener;.ALT:Ljava/lang/Object; 342
-Landroid/text/method/MetaKeyKeyListener;.SELECTING:Ljava/lang/Object; 342
-Landroid/text/method/MetaKeyKeyListener;.CAP:Ljava/lang/Object; 342
-Landroid/widget/PopupWindow$PopupBackgroundView; 343
-Landroid/widget/TextView;.TEMP_RECTF:Landroid/graphics/RectF; 343
-Landroid/text/method/ScrollingMovementMethod; 343
-Landroid/icu/impl/locale/UnicodeLocaleExtension;.EMPTY_SORTED_SET:Ljava/util/SortedSet;.m:Ljava/util/NavigableMap; 343
-Landroid/widget/PopupWindow$PopupDecorView; 343
-Landroid/widget/Editor$TextRenderNode; 343
-Landroid/widget/Editor$PositionListener; 344
-Landroid/text/style/SpellCheckSpan; 345
-Landroid/text/method/ArrowKeyMovementMethod; 346
-Landroid/text/method/TextKeyListener;.sInstance:[Landroid/text/method/TextKeyListener; 346
-Landroid/text/TextUtils$TruncateAt;.MARQUEE:Landroid/text/TextUtils$TruncateAt; 347
-Landroid/view/autofill/Helper; 348
-Lcom/android/internal/util/LatencyTracker; 349
-Lcom/android/internal/util/LatencyTracker$SLatencyTrackerHolder; 349
-Landroid/graphics/drawable/Icon; 350
-Landroid/text/style/AlignmentSpan; 351
-Landroid/text/MeasuredParagraph;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 352
-Landroid/text/MeasuredParagraph;.sPool:Landroid/util/Pools$SynchronizedPool; 352
-Landroid/icu/impl/ICUResourceBundleReader;.CACHE:Landroid/icu/impl/ICUResourceBundleReader$ReaderCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 353
-Landroid/location/ILocationManager$Stub;.dexCache:Ljava/lang/Object; 354
-Landroid/annotation/CurrentTimeMillisLong; 355
-Ljava/lang/reflect/Method; 356
-Lcom/android/internal/os/ZygoteInit; 356
-Landroid/database/DatabaseUtils; 356
-Landroid/os/HandlerThread; 356
-Ljava/security/Signature;.signatureInfo:Ljava/util/Map;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node; 357
-Ljava/security/Signature;.signatureInfo:Ljava/util/Map; 358
-Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.queue:Ljava/lang/ref/ReferenceQueue; 359
-Landroid/telephony/TelephonyRegistryManager; 360
-Landroid/graphics/HardwareRenderer; 361
-Landroid/os/BinderProxy; 362
-Landroid/app/compat/CompatChanges;.QUERY_CACHE:Landroid/app/compat/ChangeIdStateCache;.mCache:Ljava/util/LinkedHashMap; 363
-Landroid/app/compat/CompatChanges;.QUERY_CACHE:Landroid/app/compat/ChangeIdStateCache; 363
-Landroid/app/AlarmManager; 364
-Landroid/net/metrics/DhcpClientEvent; 365
-[I 366
-Landroid/media/MediaCodecList; 367
-Landroid/graphics/drawable/InsetDrawable; 368
-Landroid/widget/ProgressBar$SavedState; 369
-Landroid/widget/ScrollView$SavedState; 370
-Landroid/graphics/drawable/AnimatedVectorDrawable; 371
-Landroid/widget/ListView; 372
-Landroid/widget/AbsListView; 373
-Landroid/widget/AbsListView$SavedState; 373
-Landroid/widget/CompoundButton$SavedState; 374
-Landroid/widget/HorizontalScrollView$SavedState; 375
-Landroid/widget/HorizontalScrollView; 376
-Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.1:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 377
-Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.6:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 377
-Landroid/view/View$OnSystemUiVisibilityChangeListener; 378
-Ljava/util/AbstractMap; 379
-Landroid/telephony/euicc/EuiccCardManager$ResultCallback; 380
-Ljava/lang/Character$UnicodeBlock;.CJK_SYMBOLS_AND_PUNCTUATION:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.KANBUN:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.HANGUL_COMPATIBILITY_JAMO:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.KATAKANA:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.HANGUL_SYLLABLES:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.ENCLOSED_CJK_LETTERS_AND_MONTHS:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.HANGUL_JAMO:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.BOPOMOFO_EXTENDED:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.CJK_COMPATIBILITY_FORMS:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.BOPOMOFO:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.HIRAGANA:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.HALFWIDTH_AND_FULLWIDTH_FORMS:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.KANGXI_RADICALS:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.CJK_RADICALS_SUPPLEMENT:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.KATAKANA_PHONETIC_EXTENSIONS:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.CJK_COMPATIBILITY:Ljava/lang/Character$UnicodeBlock; 381
-Ljava/lang/Character$UnicodeBlock;.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:Ljava/lang/Character$UnicodeBlock; 382
-Ljava/lang/Character$UnicodeBlock;.CJK_COMPATIBILITY_IDEOGRAPHS:Ljava/lang/Character$UnicodeBlock; 382
-Ljava/lang/Character$UnicodeBlock;.CJK_UNIFIED_IDEOGRAPHS:Ljava/lang/Character$UnicodeBlock; 382
-Ljava/lang/Character$UnicodeBlock;.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:Ljava/lang/Character$UnicodeBlock; 382
-Ljava/lang/Character$UnicodeBlock;.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:Ljava/lang/Character$UnicodeBlock; 382
-Lcom/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry; 383
-Landroid/view/Window$DecorCallback; 383
-Landroid/view/inputmethod/EditorInfo; 383
-Landroid/view/MenuItem$OnActionExpandListener; 384
-Ljava/util/Locale;.JAPANESE:Ljava/util/Locale; 385
-Ljava/util/Locale;.KOREAN:Ljava/util/Locale; 385
-Lcom/android/internal/config/appcloning/AppCloningDeviceConfigHelper; 386
-Landroid/telecom/PhoneAccountHandle; 387
-Landroid/content/AsyncQueryHandler; 388
-Landroid/speech/RecognitionListener; 389
-Ljava/lang/InstantiationException; 390
-Ljava/util/concurrent/ExecutionException; 391
-Landroid/icu/text/DateIntervalInfo;.DIICACHE:Landroid/icu/impl/ICUCache; 392
-Landroid/text/format/DateIntervalFormat;.CACHED_FORMATTERS:Landroid/util/LruCache;.map:Ljava/util/LinkedHashMap; 392
-Landroid/icu/text/DateIntervalFormat;.LOCAL_PATTERN_CACHE:Landroid/icu/impl/ICUCache; 392
-Landroid/icu/impl/OlsonTimeZone; 392
-Landroid/text/format/DateIntervalFormat;.CACHED_FORMATTERS:Landroid/util/LruCache; 392
-Landroid/graphics/drawable/Drawable; 393
-Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 394
-Landroid/app/Activity; 395
-Landroid/icu/text/PluralRules$KeywordStatus;.INVALID:Landroid/icu/text/PluralRules$KeywordStatus;.name:Ljava/lang/String; 396
-Landroid/net/Uri; 396
-Lsun/util/calendar/CalendarSystem;.calendars:Ljava/util/concurrent/ConcurrentMap; 396
-Landroid/animation/PropertyValuesHolder$IntPropertyValuesHolder;.sJNISetterPropertyMap:Ljava/util/HashMap; 397
-Landroid/graphics/drawable/ShapeDrawable; 398
-Lcom/android/internal/widget/ActionBarContextView; 399
-Landroid/widget/Toolbar$SavedState; 399
-Lcom/android/internal/widget/ActionBarContainer; 399
-Lcom/android/internal/widget/ActionBarOverlayLayout; 399
-Lcom/android/internal/widget/ActionBarContainer$ActionBarBackgroundDrawable; 399
-Landroid/widget/ActionMenuPresenter$OverflowMenuButton; 400
-Landroid/widget/ActionMenuView; 401
-Landroid/content/res/Configuration; 402
-Ljava/util/IdentityHashMap;.NULL_KEY:Ljava/lang/Object; 403
-Ljava/util/concurrent/ForkJoinPool; 404
-Landroid/os/ResultReceiver; 405
-Ljava/util/concurrent/TimeoutException; 406
-Ljava/io/IOException; 407
-Landroid/accounts/AccountAuthenticatorResponse; 408
-Landroid/nfc/NfcAdapter; 409
-Landroid/nfc/NfcAdapter;.sNfcAdapters:Ljava/util/HashMap; 409
-Landroid/app/backup/BackupManager; 410
-Landroid/app/NotificationChannelGroup; 411
-Landroid/content/pm/ParceledListSlice; 411
-Landroid/os/FileObserver; 412
-Landroid/os/UserHandle; 413
-Landroid/content/pm/PackageManager$NameNotFoundException; 414
-[Ljava/lang/Integer; 415
-Landroid/animation/PropertyValuesHolder;.sSetterPropertyMap:Ljava/util/HashMap; 415
-Landroid/content/LocusId; 416
-Landroid/view/contentcapture/ContentCaptureContext; 416
-Landroid/telephony/ims/RegistrationManager;.IMS_REG_TO_ACCESS_TYPE_MAP:Ljava/util/Map;.table:[Ljava/lang/Object;.18:Ljava/lang/Integer; 417
-Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.4:Ljava/util/concurrent/ConcurrentHashMap$Node; 418
-Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node; 418
-Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.2:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node; 418
-Lcom/android/internal/telephony/cdnr/CarrierDisplayNameResolver;.EF_SOURCE_PRIORITY:Ljava/util/List;.a:[Ljava/lang/Object;.9:Ljava/lang/Integer; 418
-Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.56:Ljava/util/concurrent/ConcurrentHashMap$Node; 418
-Lcom/android/internal/telephony/cdnr/CarrierDisplayNameResolver;.EF_SOURCE_PRIORITY:Ljava/util/List;.a:[Ljava/lang/Object;.5:Ljava/lang/Integer; 418
-Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap; 418
-Landroid/widget/EditText; 419
-Landroid/widget/CheckedTextView; 420
-Landroid/os/strictmode/UnsafeIntentLaunchViolation; 421
-Landroid/app/Service; 422
-Ldalvik/system/BlockGuard; 423
-Landroid/hardware/devicestate/DeviceStateManagerGlobal; 424
-Landroid/hardware/camera2/CameraCharacteristics;.SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 425
-Landroid/hardware/camera2/marshal/MarshalRegistry;.sMarshalerMap:Ljava/util/HashMap; 425
-Landroid/hardware/camera2/CameraCharacteristics;.LENS_FACING:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 425
-Landroid/content/ClipboardManager$OnPrimaryClipChangedListener; 426
-Landroid/icu/text/BreakIterator;.iterCache:[Landroid/icu/impl/CacheValue; 427
-Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.13:Ljava/util/WeakHashMap$Entry; 428
-Landroid/icu/text/Collator; 429
-Landroid/icu/impl/number/parse/NanMatcher;.DEFAULT:Landroid/icu/impl/number/parse/NanMatcher;.uniSet:Landroid/icu/text/UnicodeSet;.strings:Ljava/util/SortedSet;.c:Ljava/util/Collection;.m:Ljava/util/NavigableMap; 430
-Ljava/io/FileNotFoundException; 431
-Landroid/os/BaseBundle; 432
-Landroid/service/watchdog/ExplicitHealthCheckService$PackageConfig; 433
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.116:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.12:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.90:Ljava/lang/String; 434
-Landroid/icu/text/MessageFormat;.rootLocale:Ljava/util/Locale;.baseLocale:Lsun/util/locale/BaseLocale;.language:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.385:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.107:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.112:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.480:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.550:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.143:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._obsoleteLanguages:[Ljava/lang/String;.1:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.473:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.138:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.204:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.71:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.12:Ljava/lang/String; 434
-Landroid/icu/impl/duration/impl/DataRecord$ETimeLimit;.names:[Ljava/lang/String;.1:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.9:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.99:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages3:[Ljava/lang/String;.152:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.256:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.170:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.220:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.461:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._replacementLanguages:[Ljava/lang/String;.5:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.190:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.157:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._obsoleteCountries:[Ljava/lang/String;.4:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.196:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.117:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.5:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.499:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.199:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.18:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.324:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.101:Ljava/lang/String; 434
-Landroid/icu/impl/locale/UnicodeLocaleExtension;.CA_JAPANESE:Landroid/icu/impl/locale/UnicodeLocaleExtension;._keywords:Ljava/util/SortedMap;.root:Ljava/util/TreeMap$TreeMapEntry;.key:Ljava/lang/Object; 434
-Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.3:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.140:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.105:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.37:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.5:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.22:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.103:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.412:Ljava/lang/String; 434
-Landroid/icu/impl/duration/impl/DataRecord$EMilliSupport;.names:[Ljava/lang/String;.1:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.124:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.232:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.219:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.179:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.523:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.75:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.486:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.166:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.112:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.119:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.160:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.298:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.257:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.182:Ljava/lang/String; 434
-Landroid/icu/impl/units/UnitPreferences;.measurementSystem:Ljava/util/Map;.m:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.5:Ljava/util/HashMap$Node;.next:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.47:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.180:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.111:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.358:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.96:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._obsoleteLanguages:[Ljava/lang/String;.0:Ljava/lang/String; 434
-Landroid/icu/text/DateFormat;.HOUR_GENERIC_TZ:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.67:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.254:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.222:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.55:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.349:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.16:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.352:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.443:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.478:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.19:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.401:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.137:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.65:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.474:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.168:Ljava/lang/String; 434
-Landroid/icu/impl/units/UnitPreferences;.measurementSystem:Ljava/util/Map;.m:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.15:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.111:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages3:[Ljava/lang/String;.545:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.30:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.469:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.21:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.69:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.56:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.519:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._replacementLanguages:[Ljava/lang/String;.4:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.107:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.290:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.59:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.220:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.186:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.516:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.181:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.199:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.396:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.117:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.227:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.331:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.447:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.151:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.144:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.132:Ljava/lang/String; 434
-Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.230:Ljava/lang/String; 434
-Landroid/icu/text/DateFormat;.MINUTE_SECOND:Ljava/lang/String; 434
-Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 435
-Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.head:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 436
-Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 437
-Landroid/graphics/drawable/ColorStateListDrawable; 438
-Ljava/lang/SecurityException; 439
-Ljava/lang/RuntimeException; 440
-Landroid/media/audiopolicy/AudioProductStrategy; 441
-Landroid/os/PersistableBundle; 442
-Landroid/content/pm/ShortcutInfo; 442
-Landroid/icu/text/TimeZoneFormat;._tzfCache:Landroid/icu/text/TimeZoneFormat$TimeZoneFormatCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 443
-Landroid/graphics/LeakyTypefaceStorage;.sStorage:Ljava/util/ArrayList; 443
-Landroid/graphics/LeakyTypefaceStorage;.sTypefaceMap:Landroid/util/ArrayMap; 443
-Landroid/text/TextWatcher; 444
-Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener; 445
-Ljavax/net/SocketFactory; 446
-Ljava/util/Collections; 447
-Ljava/lang/Exception; 448
-Landroid/os/UserManager; 449
-Landroid/os/RemoteException; 450
-Landroid/content/AttributionSource; 451
-Lcom/android/okhttp/internalandroidapi/HttpURLConnectionFactory$DnsAdapter; 452
-Lcom/android/okhttp/Protocol;.HTTP_2:Lcom/android/okhttp/Protocol; 452
-Ljava/net/Inet4Address; 452
-Lcom/android/okhttp/Protocol;.SPDY_3:Lcom/android/okhttp/Protocol; 452
-Lcom/android/okhttp/OkHttpClient; 452
-Landroid/os/storage/VolumeInfo; 453
-Landroid/os/storage/DiskInfo; 453
-Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mMap:Landroid/util/ArrayMap; 454
-Ljava/nio/file/StandardOpenOption;.WRITE:Ljava/nio/file/StandardOpenOption; 455
-Ljava/nio/file/StandardOpenOption;.APPEND:Ljava/nio/file/StandardOpenOption; 456
-Ljava/util/logging/FileHandler; 457
-Ljava/nio/file/StandardOpenOption;.CREATE_NEW:Ljava/nio/file/StandardOpenOption; 457
-Ljava/util/logging/FileHandler;.locks:Ljava/util/Set;.map:Ljava/util/HashMap; 457
-Lsun/nio/ch/SharedFileLockTable;.queue:Ljava/lang/ref/ReferenceQueue; 458
-Ljavax/net/ServerSocketFactory; 458
-Landroid/os/AsyncTask; 459
-Landroid/os/strictmode/UnbufferedIoViolation; 460
-Landroid/app/usage/AppStandbyInfo; 461
-Landroid/text/format/DateUtils; 462
-Landroid/security/IKeyChainService; 463
-Landroid/util/Log$TerribleFailure; 464
-Lcom/android/internal/os/RuntimeInit$KillApplicationHandler; 464
-Ljava/util/Timer;.nextSerialNumber:Ljava/util/concurrent/atomic/AtomicInteger; 465
-Landroid/telephony/ims/stub/ImsConfigImplBase$ImsConfigStub; 466
-Landroid/telephony/ims/stub/ImsRegistrationImplBase$1; 466
-Landroid/telephony/ims/ImsUtListener; 466
-Landroid/telephony/ims/feature/MmTelFeature$1; 466
-Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 467
-Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray; 467
-Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray;.mKeys:[I 467
-Landroid/telephony/ims/aidl/IImsConfig$Stub$Proxy; 468
-Landroid/telephony/ims/aidl/IImsRegistration$Stub$Proxy; 468
-Landroid/telephony/NetworkService; 469
-Landroid/telephony/TelephonyCallback$ServiceStateListener; 470
-Landroid/telephony/TelephonyCallback$PhysicalChannelConfigListener; 471
-Landroid/telephony/TelephonyCallback$RadioPowerStateListener; 471
-Lsun/security/x509/X500Name;.internedOIDs:Ljava/util/Map; 472
-Lsun/security/x509/X500Name;.internedOIDs:Ljava/util/Map;.table:[Ljava/util/HashMap$Node; 472
-Landroid/media/MediaCodec; 473
-Ljava/nio/file/StandardOpenOption;.CREATE:Ljava/nio/file/StandardOpenOption; 474
-Ljava/nio/file/NoSuchFileException; 475
-Ljava/text/DateFormatSymbols;.cachedInstances:Ljava/util/concurrent/ConcurrentMap; 476
-Ljava/util/Currency;.instances:Ljava/util/concurrent/ConcurrentMap; 476
-Ljava/util/Calendar;.cachedLocaleData:Ljava/util/concurrent/ConcurrentMap; 476
-Ljava/text/SimpleDateFormat;.cachedNumberFormatData:Ljava/util/concurrent/ConcurrentMap; 476
-Landroid/app/UriGrantsManager;.IUriGrantsManagerSingleton:Landroid/util/Singleton; 477
-Landroid/content/ContentProviderProxy; 478
-Landroid/os/DeadObjectException; 479
-Landroid/app/slice/SliceSpec; 479
-Landroid/database/sqlite/SQLiteDatabase; 480
-Ljava/util/Locale;.CHINA:Ljava/util/Locale; 481
-Ljava/util/Locale;.TAIWAN:Ljava/util/Locale; 481
-Ljava/util/Locale;.KOREA:Ljava/util/Locale; 481
-Ljava/util/Scanner; 482
-Ljava/math/BigDecimal; 483
-Ljava/security/interfaces/RSAPrivateCrtKey; 483
-Ljava/security/interfaces/RSAPrivateKey; 483
-Lcom/android/server/backup/AccountSyncSettingsBackupHelper;.KEY_ACCOUNT_TYPE:Ljava/lang/String; 483
-Landroid/util/UtilConfig; 484
-Ljava/net/ResponseCache; 485
-Landroid/content/ReceiverCallNotAllowedException; 486
-Landroid/app/ReceiverRestrictedContext; 487
-Landroid/os/strictmode/CredentialProtectedWhileLockedViolation; 488
-Landroid/app/Application; 489
-Ljava/util/NoSuchElementException; 490
-Landroid/os/Messenger; 491
-Landroid/telephony/TelephonyCallback$DataEnabledListener; 491
-Landroid/system/StructLinger; 492
diff --git a/config/dirty-image-objects.txt b/config/dirty-image-objects.txt
new file mode 100644
index 0000000..2c51511
--- /dev/null
+++ b/config/dirty-image-objects.txt
@@ -0,0 +1,1391 @@
+#
+# Copyright (C) 2024 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.
+#
+#
+#
+# Framework dirty-image-objects file for boot image.
+# The image writer will bin these objects together in the image.
+# There are two dirty-image-objects files:
+# 1) art/build/apex/dirty-image-objects.txt - contains classes and objects
+# referenced by classes defined in the ART module.
+# 2) frameworks/base/config/dirty-image-objects.txt - contains classes and
+# objects referenced by classes defined in framework modules.
+#
+# More info about dirty objects format and how to collect the data can be
+# found in: art/imgdiag/dirty_image_objects.md
+# This particular file was generated by running top 100 Android apps.
+#
+Landroid/util/Base64; 0
+Landroid/os/Debug; 1
+Landroid/content/pm/ResolveInfo; 4
+Landroid/widget/AutoCompleteTextView; 7
+Landroid/widget/ToggleButton; 8
+Landroid/widget/MultiAutoCompleteTextView; 9
+Landroid/speech/RecognitionListener; 10
+Landroid/icu/util/Calendar;.PATTERN_CACHE:Landroid/icu/impl/ICUCache; 12
+Landroid/icu/impl/DateNumberFormat;.CACHE:Landroid/icu/impl/SimpleCache; 13
+Landroid/view/ViewTreeObserver$OnWindowVisibilityChangeListener; 14
+Landroid/window/WindowContainerTransaction$Change; 14
+Landroid/window/WindowOrganizer;.IWindowOrganizerControllerSingleton:Landroid/util/Singleton; 14
+Landroid/widget/Toast; 14
+Landroid/app/smartspace/SmartspaceSession$OnTargetsAvailableListener; 14
+Landroid/view/CrossWindowBlurListeners; 15
+Landroid/text/TextUtils$TruncateAt; 16
+Landroid/app/smartspace/SmartspaceTarget; 17
+Landroid/app/smartspace/uitemplatedata/BaseTemplateData; 17
+Landroid/content/res/Resources$NotFoundException; 18
+Landroid/app/prediction/AppTarget; 18
+Landroid/icu/impl/number/parse/NanMatcher;.DEFAULT:Landroid/icu/impl/number/parse/NanMatcher;.uniSet:Landroid/icu/text/UnicodeSet;.strings:Ljava/util/SortedSet;.c:Ljava/util/Collection;.m:Ljava/util/NavigableMap; 19
+Landroid/app/prediction/AppTargetEvent; 19
+Landroid/content/res/Configuration; 19
+Landroid/os/strictmode/UnbufferedIoViolation; 21
+Lcom/android/internal/os/ProcessCpuTracker$FilterStats; 22
+Lcom/android/internal/view/WindowManagerPolicyThread; 22
+Landroid/content/pm/ShortcutServiceInternal; 22
+Landroid/app/ServiceStartArgs; 22
+Lcom/android/internal/util/ToBooleanFunction; 22
+Landroid/app/ActivityManager$RecentTaskInfo; 22
+Landroid/telecom/Logging/EventManager$EventListener; 22
+Landroid/app/assist/AssistStructure; 22
+Landroid/app/Notification$DecoratedMediaCustomViewStyle; 22
+Landroid/annotation/StringRes; 22
+Landroid/util/MemoryIntArray; 22
+Landroid/graphics/Region;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 22
+Landroid/view/RoundedCorners; 22
+Landroid/media/AudioSystem$DynamicPolicyCallback; 22
+Lcom/android/internal/R$styleable;.Window:[I 22
+Landroid/app/servertransaction/PauseActivityItem; 22
+Landroid/content/pm/PermissionGroupInfo; 22
+Landroid/app/job/JobInfo; 22
+Lcom/android/server/criticalevents/nano/CriticalEventLogProto; 22
+Lcom/android/internal/infra/ServiceConnector$Impl$CompletionAwareJob; 22
+Landroid/net/metrics/NetworkEvent; 22
+Landroid/hardware/camera2/CameraManager$CameraManagerGlobal; 22
+Landroid/debug/AdbManagerInternal; 22
+Landroid/app/ActivityManagerInternal; 22
+Landroid/hardware/soundtrigger/SoundTrigger$StatusListener; 22
+Landroid/hardware/display/DisplayManagerInternal; 22
+Landroid/hardware/location/IActivityRecognitionHardwareClient; 22
+Landroid/hardware/sidekick/SidekickInternal; 22
+Landroid/media/AudioSystem; 22
+Landroid/net/Uri$PathPart;.NULL:Landroid/net/Uri$PathPart; 22
+Lcom/android/internal/os/LongArrayMultiStateCounter; 22
+Landroid/hardware/location/NanoAppMessage; 22
+Lcom/android/internal/widget/LockSettingsInternal; 22
+Landroid/accounts/AuthenticatorException; 22
+Landroid/window/DisplayAreaAppearedInfo; 22
+Landroid/content/pm/RegisteredServicesCache$2; 22
+Landroid/permission/PermissionManagerInternal; 22
+Landroid/content/pm/CrossProfileAppsInternal; 22
+Landroid/app/assist/ActivityId; 22
+Landroid/os/Process;.ZYGOTE_PROCESS:Landroid/os/ZygoteProcess; 22
+Landroid/appwidget/AppWidgetManagerInternal; 22
+Landroid/net/metrics/ValidationProbeEvent; 22
+Landroid/webkit/WebViewLibraryLoader$RelroFileCreator; 22
+Landroid/graphics/GraphicsStatsService; 22
+Lcom/android/internal/os/LongArrayMultiStateCounter$LongArrayContainer; 22
+Landroid/content/pm/PackageManager$Property; 22
+Landroid/app/servertransaction/DestroyActivityItem; 22
+Landroid/os/PowerManagerInternal; 22
+Landroid/app/SystemServiceRegistry; 22
+Landroid/hardware/location/GeofenceHardwareImpl; 22
+Landroid/app/AppOpsManager$NoteOpEvent; 22
+Lcom/android/framework/protobuf/nano/MessageNano; 22
+Landroid/content/pm/RegisteredServicesCache$3; 22
+Landroid/content/pm/PackageManager;.sCacheAutoCorker:Landroid/app/PropertyInvalidatedCache$AutoCorker; 22
+Landroid/accounts/AccountManagerInternal; 22
+Landroid/app/PropertyInvalidatedCache;.sCorkedInvalidates:Ljava/util/HashMap; 22
+Landroid/media/audiopolicy/AudioVolumeGroup; 22
+Landroid/app/admin/DevicePolicyManagerInternal$OnCrossProfileWidgetProvidersChangeListener; 22
+Landroid/content/pm/dex/ArtManagerInternal; 22
+Landroid/app/UriGrantsManager;.IUriGrantsManagerSingleton:Landroid/util/Singleton; 22
+Lcom/android/internal/os/RuntimeInit$ApplicationWtfHandler; 22
+Lcom/android/internal/app/procstats/AssociationState;.sTmpSourceKey:Lcom/android/internal/app/procstats/AssociationState$SourceKey; 22
+Landroid/app/servertransaction/StopActivityItem; 22
+Landroid/hardware/soundtrigger/SoundTriggerModule$EventHandlerDelegate; 22
+Landroid/app/PendingIntent$FinishedDispatcher; 22
+Landroid/app/servertransaction/NewIntentItem; 22
+Landroid/content/pm/UserPackage; 22
+Landroid/net/metrics/DhcpErrorEvent; 22
+Lcom/android/internal/listeners/ListenerExecutor$FailureCallback; 22
+Landroid/app/time/TimeZoneConfiguration; 22
+Landroid/net/metrics/IpManagerEvent; 22
+Landroid/view/Display$HdrCapabilities; 22
+Landroid/media/AudioSystem$AudioRecordingCallback; 22
+Landroid/telecom/PhoneAccountHandle; 22
+Lcom/android/server/criticalevents/nano/CriticalEventProto$InstallPackages; 22
+Landroid/os/BatteryManagerInternal; 22
+Landroid/provider/Settings; 22
+Lcom/android/internal/util/function/LongObjPredicate; 22
+Landroid/view/WindowManagerPolicyConstants; 22
+Landroid/accessibilityservice/AccessibilityServiceInfo; 22
+Lcom/android/internal/os/CachedDeviceState$Readonly; 22
+Landroid/service/voice/VoiceInteractionManagerInternal; 22
+Landroid/service/notification/Condition; 22
+Landroid/util/EventLog; 22
+Lcom/android/server/AppWidgetBackupBridge; 22
+Landroid/net/wifi/nl80211/WifiNl80211Manager$ScanEventCallback; 22
+Lcom/android/internal/os/StatsdHiddenApiUsageLogger;.sInstance:Lcom/android/internal/os/StatsdHiddenApiUsageLogger; 22
+Landroid/app/AppOpsManager$AttributedOpEntry; 22
+Landroid/attention/AttentionManagerInternal; 22
+Landroid/os/PatternMatcher;.sParsedPatternScratch:[I 22
+Landroid/view/DisplayAddress$Physical; 22
+Lcom/android/internal/util/function/DodecConsumer; 22
+Landroid/app/servertransaction/StartActivityItem; 22
+Landroid/window/IOnBackInvokedCallback$Stub$Proxy; 22
+Landroid/content/ComponentName$WithComponentName; 22
+Landroid/media/AudioPlaybackConfiguration$PlayerDeathMonitor; 22
+Landroid/telecom/Log; 22
+Landroid/view/ViewDebug$ExportedProperty; 22
+Landroid/graphics/Region;.sPool:Landroid/util/Pools$SynchronizedPool; 22
+Lcom/android/internal/os/LongMultiStateCounter; 22
+Landroid/content/pm/parsing/ApkLite; 22
+Landroid/app/PropertyInvalidatedCache;.sCorks:Ljava/util/HashMap; 22
+Landroid/content/pm/PackageInstaller$SessionInfo; 22
+Landroid/os/RemoteCallback$1; 22
+Landroid/os/UEventObserver; 22
+Landroid/app/AppOpsManagerInternal; 22
+Lcom/android/internal/infra/AbstractRemoteService$VultureCallback; 22
+Landroid/media/AudioSystem$ErrorCallback; 22
+Landroid/window/TaskAppearedInfo; 22
+Landroid/content/pm/ShortcutServiceInternal$ShortcutChangeListener; 22
+Landroid/service/notification/NotificationListenerService$RankingMap; 22
+Landroid/app/PropertyInvalidatedCache$AutoCorker$1; 22
+Landroid/view/autofill/AutofillManagerInternal; 22
+Lcom/android/internal/content/om/OverlayConfig$PackageProvider; 22
+Lcom/android/internal/os/BatteryStatsHistory$HistoryStepDetailsCalculator; 22
+Lcom/android/internal/os/KernelCpuBpfTracking; 22
+Landroid/os/ServiceSpecificException; 22
+Landroid/content/pm/RegisteredServicesCache$1; 22
+Landroid/app/time/TimeZoneCapabilities; 22
+Landroid/content/res/ResourceTimer; 22
+Landroid/app/servertransaction/ConfigurationChangeItem; 22
+Lcom/android/internal/infra/AndroidFuture$1; 22
+Landroid/content/pm/PackageManager;.sCacheAutoCorker:Landroid/app/PropertyInvalidatedCache$AutoCorker;.mLock:Ljava/lang/Object; 22
+Lcom/android/internal/content/om/OverlayConfig; 22
+Landroid/app/ActivityManager; 22
+Landroid/net/metrics/ApfProgramEvent; 22
+Landroid/graphics/Bitmap$CompressFormat; 22
+Landroid/window/ITaskOrganizer$Stub$Proxy; 22
+Landroid/app/servertransaction/ActivityResultItem; 22
+Landroid/service/notification/ConditionProviderService; 22
+Landroid/hardware/display/DeviceProductInfo; 22
+Landroid/hardware/display/DeviceProductInfo$ManufactureDate; 22
+Landroid/app/ApplicationExitInfo; 22
+Landroid/app/admin/DevicePolicyManagerInternal; 22
+Lcom/android/server/usage/AppStandbyInternal; 22
+Landroid/app/PropertyInvalidatedCache;.sCorkLock:Ljava/lang/Object; 22
+Lcom/android/internal/display/BrightnessSynchronizer; 22
+Landroid/service/notification/StatusBarNotification; 22
+Landroid/os/FileUtils$ProgressListener; 22
+Landroid/media/AudioAttributes; 22
+Landroid/service/autofill/FillContext; 22
+Landroid/hardware/biometrics/BiometricSourceType; 22
+Landroid/hardware/location/GeofenceHardwareService; 22
+Landroid/app/servertransaction/ActivityConfigurationChangeItem; 22
+Landroid/content/pm/LauncherActivityInfoInternal; 22
+Landroid/telecom/Logging/SessionManager$ISessionListener; 22
+Landroid/text/format/TimeFormatter; 22
+Landroid/hardware/location/ContextHubInfo; 22
+Landroid/os/ServiceManager$ServiceNotFoundException; 22
+Landroid/hardware/biometrics/ComponentInfoInternal; 22
+Lcom/android/internal/os/LongArrayMultiStateCounter;.sTmpArrayContainer:Ljava/util/concurrent/atomic/AtomicReference; 22
+Lcom/android/internal/config/appcloning/AppCloningDeviceConfigHelper; 22
+Landroid/os/storage/StorageVolume; 22
+Landroid/app/AppOpsManager$SamplingStrategy; 22
+Lcom/android/server/LocalServices;.sLocalServiceObjects:Landroid/util/ArrayMap; 22
+Landroid/media/AudioPlaybackConfiguration; 22
+Landroid/view/ViewDebug$FlagToString; 22
+Landroid/service/dreams/DreamManagerInternal; 22
+Lcom/android/server/criticalevents/nano/CriticalEventProto$NativeCrash; 22
+Landroid/net/wifi/nl80211/WifiNl80211Manager$ScanEventHandler; 22
+Landroid/util/ArrayMap;.sBaseCacheLock:Ljava/lang/Object; 22
+Landroid/content/pm/FallbackCategoryProvider;.sFallbacks:Landroid/util/ArrayMap; 22
+Landroid/os/SharedMemory; 22
+Landroid/media/AudioManagerInternal$RingerModeDelegate; 22
+Landroid/util/NtpTrustedTime; 22
+Landroid/appwidget/AppWidgetProviderInfo; 22
+Landroid/telephony/ServiceState; 22
+Landroid/service/notification/ZenPolicy; 22
+Lcom/android/internal/statusbar/NotificationVisibility$NotificationLocation; 22
+Landroid/content/pm/UserPackage;.sCacheLock:Ljava/lang/Object; 22
+Lcom/android/server/WidgetBackupProvider; 22
+Lcom/android/internal/os/LooperStats; 22
+Lcom/android/internal/infra/AbstractRemoteService$AsyncRequest; 22
+Landroid/content/pm/BaseParceledListSlice$1; 22
+Landroid/media/AudioManagerInternal; 22
+Landroid/content/pm/UserPackage;.sCache:Landroid/util/SparseArrayMap;.mData:Landroid/util/SparseArray; 22
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.5:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mLock:Ljava/lang/Object; 22
+Landroid/content/pm/RegisteredServicesCacheListener; 22
+Landroid/view/WindowManagerPolicyConstants$PointerEventListener; 22
+Landroid/accounts/AccountManagerInternal$OnAppPermissionChangeListener; 22
+Landroid/database/ContentObserver$$ExternalSyntheticLambda1; 22
+Landroid/webkit/WebViewZygote; 22
+Landroid/content/pm/LauncherApps$ShortcutQuery$QueryFlags; 22
+Lcom/android/server/criticalevents/nano/CriticalEventProto$SystemServerStarted; 22
+Landroid/os/storage/StorageManagerInternal; 22
+Landroid/service/notification/ZenModeConfig; 22
+Landroid/icu/text/MessagePattern;.argTypes:[Landroid/icu/text/MessagePattern$ArgType;.0:Landroid/icu/text/MessagePattern$ArgType;.name:Ljava/lang/String; 27
+Lcom/android/internal/app/procstats/DumpUtils;.STATE_NAMES_CSV:[Ljava/lang/String;.12:Ljava/lang/String; 27
+Landroid/media/MediaDrm; 27
+Landroid/icu/util/GenderInfo$Gender;.OTHER:Landroid/icu/util/GenderInfo$Gender;.name:Ljava/lang/String; 27
+Landroid/provider/DocumentsContract;.DOWNLOADS_PROVIDER_AUTHORITY:Ljava/lang/String; 27
+Landroid/annotation/SystemApi; 27
+Landroid/icu/util/MeasureUnit$Complexity;.MIXED:Landroid/icu/util/MeasureUnit$Complexity;.name:Ljava/lang/String; 27
+Landroid/webkit/WebViewFactory;.sTimestamps:Landroid/webkit/WebViewFactory$StartupTimestamps; 55
+Landroid/webkit/WebViewFactoryProvider; 56
+Landroid/webkit/WebViewFactory; 57
+Landroid/webkit/WebViewProvider$ScrollDelegate; 58
+Landroid/webkit/WebViewProvider; 58
+Landroid/webkit/WebViewProvider$ViewDelegate; 58
+Landroid/webkit/CookieSyncManager; 59
+Landroid/view/PointerIcon;.SYSTEM_ICONS:Landroid/util/SparseArray; 60
+Landroid/webkit/WebView; 61
+Landroid/webkit/WebResourceRequest; 62
+Landroid/webkit/DownloadListener; 63
+Landroid/webkit/WebViewFactoryProvider$Statics; 64
+Landroid/window/WindowTokenClientController; 65
+Landroid/os/PowerManager$OnThermalStatusChangedListener; 66
+Landroid/content/ClipboardManager$OnPrimaryClipChangedListener; 67
+Landroid/hardware/input/InputManager$InputDeviceListener; 68
+Landroid/hardware/input/InputManagerGlobal; 69
+Landroid/media/MediaCodecInfo$CodecCapabilities$FeatureList; 70
+Landroid/media/MediaCodecList; 70
+Landroid/media/MediaCodec; 71
+Landroid/os/ParcelFileDescriptor; 72
+Landroid/webkit/ValueCallback; 73
+Landroid/view/View$OnDragListener; 74
+Landroid/view/autofill/Helper; 75
+Landroid/app/ActivityTaskManager;.IActivityTaskManagerSingleton:Landroid/util/Singleton; 78
+Lcom/android/internal/inputmethod/ImeTracing; 79
+Landroid/view/SurfaceControlRegistry; 79
+Landroid/view/inputmethod/IInputMethodManagerGlobalInvoker; 80
+Landroid/window/BackTouchTracker; 80
+Landroid/view/Choreographer; 81
+Lcom/android/internal/policy/DecorView; 82
+Landroid/window/SurfaceSyncGroup; 82
+Landroid/view/ViewTreeObserver; 82
+Landroid/view/accessibility/AccessibilityNodeIdManager; 82
+Landroid/view/ViewRootImpl; 82
+Landroid/widget/FrameLayout; 82
+Landroid/view/ViewStub; 82
+Landroid/os/SystemProperties;.sChangeCallbacks:Ljava/util/ArrayList; 83
+Lcom/android/internal/os/SomeArgs; 84
+Landroid/view/autofill/AutofillId; 85
+Landroid/app/ActivityClient;.sInstance:Landroid/util/Singleton; 86
+Landroid/app/ActivityClient;.INTERFACE_SINGLETON:Landroid/app/ActivityClient$ActivityClientControllerSingleton; 86
+Landroid/os/StrictMode$InstanceTracker;.sInstanceCounts:Ljava/util/HashMap; 87
+Landroid/widget/LinearLayout; 88
+Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray; 89
+Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray;.mKeys:[I 89
+Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 89
+Landroid/view/accessibility/AccessibilityManager; 90
+Landroid/graphics/Bitmap; 91
+Landroid/content/SharedPreferences; 113
+Landroid/app/AppOpsManager; 115
+Landroid/os/Parcel;.mCreators:Ljava/util/HashMap; 117
+Landroid/os/GraphicsEnvironment;.sInstance:Landroid/os/GraphicsEnvironment; 117
+Landroid/os/Process; 117
+Landroid/os/Parcel;.sPairedCreators:Ljava/util/HashMap; 117
+Landroid/os/Parcel; 117
+Landroid/app/ApplicationLoaders;.gApplicationLoaders:Landroid/app/ApplicationLoaders;.mLoaders:Landroid/util/ArrayMap; 117
+Landroid/graphics/Typeface; 118
+Landroid/provider/DeviceConfigInitializer; 118
+Landroid/icu/util/TimeZone; 118
+Landroid/view/View; 118
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap; 118
+Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap; 118
+Landroid/app/ActivityThread; 118
+Landroid/util/ArraySet; 118
+Landroid/hardware/display/DisplayManagerGlobal; 118
+Landroid/os/LocaleList; 118
+Landroid/telephony/TelephonyFrameworkInitializer; 118
+Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object; 118
+Landroid/os/Binder; 118
+Landroid/os/DdmSyncState; 118
+Landroid/media/MediaFrameworkPlatformInitializer; 118
+Lcom/android/internal/os/RuntimeInit; 118
+Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map;.table:[Ljava/util/WeakHashMap$Entry; 118
+Landroid/os/Message; 118
+Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder; 118
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.5:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 118
+Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap;.mHashes:[I 118
+Landroid/ddm/DdmHandleAppName; 118
+Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map; 118
+Landroid/os/ServiceManager; 118
+Landroid/os/Looper; 118
+Landroid/se/omapi/SeFrameworkInitializer; 118
+Landroid/os/StrictMode; 118
+Landroid/os/Environment; 118
+Landroid/security/net/config/ApplicationConfig; 118
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.12:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 119
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.5:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 120
+Landroid/app/servertransaction/ClientTransactionListenerController; 121
+Lcom/android/internal/os/BinderInternal; 122
+Landroid/graphics/Compatibility; 123
+Landroid/renderscript/RenderScriptCacheDir; 123
+Landroid/app/DexLoadReporter;.INSTANCE:Landroid/app/DexLoadReporter;.mDataDirs:Ljava/util/Set;.map:Ljava/util/HashMap; 123
+Landroid/graphics/Canvas; 123
+Landroid/provider/FontsContract; 123
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 126
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 127
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.12:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 128
+Landroid/graphics/HardwareRenderer; 129
+Landroid/app/QueuedWork; 130
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.12:Ljava/util/WeakHashMap$Entry; 131
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.10:Ljava/util/WeakHashMap$Entry; 132
+Landroid/content/Context; 133
+Landroid/view/inputmethod/InputMethodManager; 134
+Landroid/view/WindowManagerGlobal; 135
+Landroid/telephony/TelephonyManager; 136
+Landroid/database/CursorWindow; 137
+Landroid/database/sqlite/SQLiteGlobal; 138
+Landroid/database/sqlite/SQLiteDebug$NoPreloadHolder; 138
+Landroid/database/sqlite/SQLiteCompatibilityWalFlags; 138
+Landroid/content/ContentResolver; 139
+Landroid/app/NotificationManager; 140
+Landroid/os/Handler; 141
+Landroid/icu/impl/locale/BaseLocale;.CACHE:Landroid/icu/impl/locale/BaseLocale$Cache;._map:Ljava/util/concurrent/ConcurrentHashMap; 142
+Landroid/graphics/Bitmap;.sAllBitmaps:Ljava/util/WeakHashMap; 143
+Landroid/text/TextUtils; 144
+Landroid/graphics/drawable/ColorDrawable; 145
+Landroid/transition/ChangeImageTransform; 146
+Landroid/transition/ChangeTransform; 146
+Landroid/transition/ChangeClipBounds; 146
+Landroid/text/TextLine;.sCached:[Landroid/text/TextLine; 147
+Landroid/graphics/TemporaryBuffer; 148
+Landroid/text/Layout;.sTempRect:Landroid/graphics/Rect; 149
+Landroid/content/res/ColorStateList;.sCache:Landroid/util/SparseArray; 150
+Landroid/widget/ImageView; 151
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.11:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 163
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.11:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 164
+Landroid/content/pm/VersionedPackage; 171
+Landroid/content/SharedPreferences$OnSharedPreferenceChangeListener; 172
+Landroid/database/sqlite/SQLiteDatabase$CursorFactory; 173
+Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle; 174
+Landroid/content/ComponentName; 175
+Landroid/icu/impl/ZoneMeta;.SYSTEM_ZONE_CACHE:Landroid/icu/impl/ZoneMeta$SystemTimeZoneCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 180
+Landroid/icu/impl/ZoneMeta;.CANONICAL_ID_CACHE:Landroid/icu/impl/ICUCache; 181
+Landroid/icu/impl/ICUResourceBundleReader;.CACHE:Landroid/icu/impl/ICUResourceBundleReader$ReaderCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 182
+Landroid/app/UiModeManager; 183
+Landroid/media/AudioManager; 184
+Landroid/util/ArrayMap; 185
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mSkips:[J 188
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.12:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mSkips:[J 189
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.5:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mSkips:[J 190
+Landroid/icu/text/DecimalFormatSymbols;.cachedLocaleData:Landroid/icu/impl/CacheBase;.map:Ljava/util/concurrent/ConcurrentHashMap; 205
+Landroid/icu/impl/CurrencyData;.provider:Landroid/icu/impl/CurrencyData$CurrencyDisplayInfoProvider; 206
+Landroid/icu/text/DateFormatSymbols;.DFSCACHE:Landroid/icu/impl/CacheBase;.map:Ljava/util/concurrent/ConcurrentHashMap; 211
+Landroid/animation/AnimatorInflater;.sTmpTypedValue:Landroid/util/TypedValue; 217
+Landroid/graphics/drawable/RippleDrawable; 218
+Landroid/text/method/SingleLineTransformationMethod; 219
+Landroid/text/style/SuggestionSpan; 220
+Landroid/text/SpannableStringBuilder;.sCachedIntBuffer:[[I 221
+Landroid/widget/TextView$ChangeWatcher; 222
+Landroid/text/DynamicLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 223
+Landroid/text/DynamicLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool; 223
+Landroid/text/DynamicLayout; 223
+Landroid/text/DynamicLayout$ChangeWatcher; 224
+Landroid/text/style/WrapTogetherSpan; 225
+Landroid/text/Selection$MemoryTextWatcher; 226
+Landroid/text/Selection;.SELECTION_MEMORY:Ljava/lang/Object; 227
+Landroid/text/Selection;.SELECTION_END:Ljava/lang/Object; 227
+Landroid/text/Selection;.SELECTION_START:Ljava/lang/Object; 227
+Landroid/text/method/TextKeyListener;.sInstance:[Landroid/text/method/TextKeyListener; 228
+Landroid/text/method/ArrowKeyMovementMethod; 228
+Landroid/view/textclassifier/TextClassificationConstants; 229
+Landroid/text/style/SpellCheckSpan; 230
+Landroid/text/TextUtils$TruncateAt;.MARQUEE:Landroid/text/TextUtils$TruncateAt; 231
+Landroid/text/style/ReplacementSpan; 232
+Landroid/text/style/LineBackgroundSpan; 233
+Landroid/text/style/LeadingMarginSpan; 234
+Landroid/text/style/TabStopSpan; 235
+Landroid/text/style/LineBreakConfigSpan; 236
+Landroid/text/style/MetricAffectingSpan; 237
+Landroid/text/style/CharacterStyle; 238
+Landroid/text/style/AlignmentSpan; 239
+Landroid/text/SpanWatcher; 240
+Lcom/android/internal/util/ArrayUtils;.sCache:[Ljava/lang/Object; 241
+Landroid/text/TextWatcher; 242
+Landroid/text/style/ClickableSpan; 243
+Landroid/text/style/LineHeightSpan; 244
+Landroid/text/method/LinkMovementMethod; 245
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.queue:Ljava/lang/ref/ReferenceQueue; 246
+Landroid/view/ViewRootImpl$$ExternalSyntheticLambda11; 247
+Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map;.queue:Ljava/lang/ref/ReferenceQueue; 249
+Landroid/app/NotificationChannel; 251
+Landroid/os/HandlerThread; 252
+Lcom/android/internal/os/ZygoteInit; 252
+Landroid/database/DatabaseUtils; 252
+Landroid/annotation/CurrentTimeMillisLong; 253
+Landroid/os/AsyncTask; 254
+Landroid/text/StaticLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 255
+Landroid/text/StaticLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool; 255
+Landroid/graphics/drawable/GradientDrawable; 256
+Landroid/graphics/drawable/StateListDrawable; 257
+Landroid/widget/RelativeLayout; 258
+Landroid/graphics/drawable/BitmapDrawable; 259
+Landroid/graphics/drawable/Drawable; 262
+Landroid/view/TextureView$SurfaceTextureListener; 265
+Landroid/graphics/SurfaceTexture; 266
+Landroid/media/audiopolicy/AudioProductStrategy; 267
+Landroid/media/PlayerBase; 268
+Landroid/os/FileUtils; 269
+Landroid/app/AppOpsManager$OnOpActiveChangedListener; 270
+Landroid/telephony/ims/ImsService;.CAPABILITIES_LOG_MAP:Ljava/util/Map;.table:[Ljava/lang/Object;.2:Ljava/lang/Long; 271
+Landroid/icu/impl/ValidIdentifiers$Datasubtype;.unknown:Landroid/icu/impl/ValidIdentifiers$Datasubtype;.name:Ljava/lang/String; 271
+Lcom/android/ims/rcs/uce/UceDeviceState;.DEVICE_STATE_DESCRIPTION:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.4:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 271
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.569:Ljava/lang/Long; 272
+Lcom/android/ims/rcs/uce/UceDeviceState;.DEVICE_STATE_DESCRIPTION:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.3:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 273
+Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.0:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 274
+Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.1:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 275
+Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.6:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 276
+Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mMap:Landroid/util/ArrayMap; 277
+Landroid/os/RemoteException; 278
+Landroid/content/pm/PackageManager$OnChecksumsReadyListener; 279
+Landroid/content/pm/Checksum$Type; 280
+Landroid/view/InputEvent;.mNextSeq:Ljava/util/concurrent/atomic/AtomicInteger; 281
+Landroid/view/MotionEvent; 282
+Landroid/animation/PropertyValuesHolder$FloatPropertyValuesHolder;.sJNISetterPropertyMap:Ljava/util/HashMap; 283
+Landroid/animation/PropertyValuesHolder;.sGetterPropertyMap:Ljava/util/HashMap; 284
+Landroid/graphics/drawable/LayerDrawable; 285
+Landroid/graphics/Bitmap;.sAllBitmaps:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry; 289
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.11:Ljava/lang/Boolean; 292
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1:Ljava/lang/Boolean; 293
+Landroid/util/TypedValue; 305
+Landroid/os/MessageQueue; 306
+Landroid/view/KeyEvent; 307
+Landroid/app/job/JobServiceEngine$JobHandler; 308
+Landroid/os/AsyncTask$InternalHandler; 309
+Landroid/app/IActivityTaskManager; 310
+Landroid/view/View$$ExternalSyntheticLambda4; 310
+Landroid/view/WindowInsets; 311
+Landroid/app/ActivityTaskManager$2; 311
+Landroid/util/Singleton; 311
+Landroid/view/View$AttachInfo; 311
+Landroid/icu/text/DecimalFormatSymbols;.DEF_DIGIT_STRINGS_ARRAY:[Ljava/lang/String;.3:Ljava/lang/String; 311
+Lcom/android/icu/util/regex/PatternNative; 311
+Landroid/icu/text/DecimalFormatSymbols;.DEF_DIGIT_STRINGS_ARRAY:[Ljava/lang/String;.2:Ljava/lang/String; 311
+Landroid/app/Dialog$ListenersHandler; 311
+Landroid/webkit/WebViewDelegate; 311
+Landroid/hardware/display/IDisplayManager; 311
+Landroid/app/servertransaction/PendingTransactionActions$StopInfo; 312
+Landroid/view/animation/Animation$3; 312
+Landroid/content/pm/IPackageManager; 313
+Landroid/view/ViewRootImpl$8; 314
+Landroid/view/ViewRootImpl$ViewRootHandler; 315
+Landroid/app/LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0; 316
+Landroid/app/LoadedApk$ServiceDispatcher$RunConnection; 317
+Landroid/view/Choreographer$FrameDisplayEventReceiver; 318
+Landroid/view/inputmethod/InputMethodManager$H; 318
+Landroid/graphics/HardwareRendererObserver$$ExternalSyntheticLambda0; 318
+Landroid/view/Choreographer$FrameHandler; 319
+Landroid/app/ActivityThread$H; 320
+Landroid/os/Parcel$ReadWriteHelper; 327
+Landroid/util/SparseArray; 328
+Landroid/app/Instrumentation; 329
+Landroid/app/IActivityManager$Stub$Proxy; 330
+Landroid/content/pm/Attribution; 331
+[Landroid/content/pm/PermissionInfo; 331
+[Landroid/os/PatternMatcher; 331
+Landroid/content/pm/ActivityInfo$WindowLayout; 331
+Landroid/content/pm/ServiceInfo; 331
+[Landroid/content/pm/PathPermission; 331
+Landroid/content/pm/SharedLibraryInfo; 331
+Landroid/content/pm/SigningInfo; 331
+Landroid/content/pm/PathPermission; 331
+Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_APERTURES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 331
+[Landroid/content/pm/ServiceInfo; 331
+[Landroid/content/pm/ProviderInfo; 331
+[Landroid/content/pm/FeatureInfo; 331
+[Landroid/content/pm/Attribution; 331
+Landroid/content/pm/ActivityInfo; 331
+Landroid/os/PatternMatcher; 331
+Landroid/content/pm/ConfigurationInfo; 331
+[Landroid/content/pm/FeatureGroupInfo; 331
+[Landroid/content/pm/InstrumentationInfo; 331
+[Landroid/content/pm/ActivityInfo; 331
+[Landroid/content/pm/ConfigurationInfo; 331
+Landroid/content/pm/InstrumentationInfo; 331
+[Landroid/content/pm/Signature; 331
+Landroid/content/pm/FeatureGroupInfo; 331
+Landroid/view/ViewManager; 332
+Landroid/view/KeyEvent$Callback; 332
+Landroid/view/ViewParent; 332
+Landroid/view/accessibility/AccessibilityEventSource; 332
+Landroid/graphics/drawable/Drawable$Callback; 332
+Landroid/content/pm/SigningDetails; 334
+Landroid/content/pm/ProviderInfo; 334
+Landroid/content/pm/PermissionInfo; 334
+Landroid/content/pm/FeatureInfo; 334
+Landroid/util/Pair; 335
+Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_FOCAL_LENGTHS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 336
+primitive F 336
+Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_CAPABILITIES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/params/StreamConfiguration; 337
+Landroid/hardware/camera2/params/StreamConfigurationDuration; 337
+Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+Landroid/hardware/camera2/params/HighSpeedVideoConfiguration; 337
+Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 337
+primitive I 337
+Landroid/hardware/camera2/CameraCharacteristics;.SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 338
+Landroid/hardware/camera2/marshal/MarshalRegistry;.sMarshalerMap:Ljava/util/HashMap; 338
+Landroid/hardware/camera2/CameraCharacteristics;.LENS_FACING:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 339
+Landroid/graphics/drawable/NinePatchDrawable; 341
+Landroid/widget/HorizontalScrollView; 342
+Lcom/android/internal/os/PowerProfile;.sPowerItemMap:Ljava/util/HashMap; 343
+Lcom/android/internal/os/PowerProfile;.sModemPowerProfile:Lcom/android/internal/power/ModemPowerProfile;.mPowerConstants:Landroid/util/SparseDoubleArray;.mValues:Landroid/util/SparseLongArray; 343
+Lcom/android/internal/os/PowerProfile;.sPowerArrayMap:Ljava/util/HashMap; 343
+Landroid/content/pm/ComponentInfo; 346
+Landroid/content/pm/Signature; 346
+Landroid/content/pm/PackageItemInfo; 347
+Landroid/content/pm/PackageInfo; 348
+Landroid/os/Parcelable; 349
+Landroid/aconfig/nano/Aconfig$parsed_flag; 355
+Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedString; 355
+Landroid/aconfig/nano/Aconfig$tracepoint; 355
+Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringValueMap; 355
+Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringSet; 355
+Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringList; 355
+Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object; 355
+Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringArray; 355
+Landroid/app/ActivityTaskManager; 355
+Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap;.mHashes:[I 355
+Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap; 355
+Lcom/android/ims/rcs/uce/presence/pidfparser/omapres/Version;.ELEMENT_NAME:Ljava/lang/String; 356
+primitive [[I 356
+Landroid/provider/SyncStateContract$Columns;.DATA:Ljava/lang/String; 356
+Landroid/view/Window$Callback; 356
+Landroid/os/BatteryConsumer;.sPowerComponentNames:[Ljava/lang/String;.13:Ljava/lang/String; 358
+Landroid/graphics/Color;.sColorNameMap:Ljava/util/HashMap;.table:[Ljava/util/HashMap$Node;.3:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 358
+Lcom/android/ims/rcs/uce/presence/pidfparser/pidf/Basic;.OPEN:Ljava/lang/String; 358
+Lcom/android/internal/os/BinderCallsStats$SettingsObserver;.SETTINGS_ENABLED_KEY:Ljava/lang/String; 358
+Landroid/os/IncidentManager;.URI_SCHEME:Ljava/lang/String; 358
+Landroid/text/Html$HtmlParser;.schema:Lorg/ccil/cowan/tagsoup/HTMLSchema;.theEntities:Ljava/util/HashMap;.table:[Ljava/util/HashMap$Node;.3233:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 358
+Landroid/icu/impl/units/UnitsData$Constants;.DEFAULT_USAGE:Ljava/lang/String; 359
+Lcom/android/internal/telephony/IccProvider;.ADDRESS_BOOK_COLUMN_NAMES:[Ljava/lang/String;.0:Ljava/lang/String; 359
+Landroid/app/NotificationChannel;.EDIT_LAUNCHER:Ljava/lang/String; 360
+Lcom/android/internal/os/RailStats;.WIFI_SUBSYSTEM:Ljava/lang/String; 360
+Lcom/android/ims/ImsUt;.KEY_ACTION:Ljava/lang/String; 360
+Landroid/view/textclassifier/TextClassifier;.TYPE_URL:Ljava/lang/String; 360
+Landroid/app/NotificationChannel;.TAG_CHANNEL:Ljava/lang/String; 360
+Landroid/icu/text/MessageFormat;.typeList:[Ljava/lang/String;.5:Ljava/lang/String; 360
+Landroid/icu/impl/ValidIdentifiers$Datatype;.region:Landroid/icu/impl/ValidIdentifiers$Datatype;.name:Ljava/lang/String; 360
+Landroid/provider/Telephony$BaseMmsColumns;.START:Ljava/lang/String; 360
+Lcom/android/ims/rcs/uce/presence/pidfparser/pidf/Timestamp;.ELEMENT_NAME:Ljava/lang/String; 360
+Lcom/android/ims/rcs/uce/presence/pidfparser/pidf/Status;.ELEMENT_NAME:Ljava/lang/String; 360
+Landroid/os/BatteryManager;.EXTRA_SEQUENCE:Ljava/lang/String; 360
+Landroid/icu/text/DecimalFormatSymbols;.DEF_DIGIT_STRINGS_ARRAY:[Ljava/lang/String;.0:Ljava/lang/String; 360
+Landroid/provider/Telephony$ThreadsColumns;.ERROR:Ljava/lang/String; 360
+Lcom/android/ims/ImsManager;.TRUE:Ljava/lang/String; 361
+Ljavax/sip/header/ContentEncodingHeader;.NAME:Ljava/lang/String; 362
+Landroid/widget/CompoundButton; 362
+Landroid/view/View$OnClickListener; 362
+Landroid/content/res/AssetManager; 363
+Landroid/view/ContextThemeWrapper; 364
+Landroid/content/res/Resources; 365
+Landroid/content/res/ResourcesImpl; 366
+Landroid/graphics/drawable/GradientDrawable$Orientation;.BOTTOM_TOP:Landroid/graphics/drawable/GradientDrawable$Orientation; 366
+Landroid/view/animation/Animation$1; 366
+Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.220:Ljava/lang/String; 366
+Landroid/graphics/drawable/GradientDrawable$Orientation;.TL_BR:Landroid/graphics/drawable/GradientDrawable$Orientation; 366
+Landroid/view/ViewRootImpl$5; 366
+Landroid/graphics/drawable/GradientDrawable$Orientation;.RIGHT_LEFT:Landroid/graphics/drawable/GradientDrawable$Orientation; 366
+Landroid/media/MediaPlayer$EventHandler; 366
+Landroid/view/View$$ExternalSyntheticLambda7; 366
+Landroid/graphics/drawable/GradientDrawable$Orientation;.BR_TL:Landroid/graphics/drawable/GradientDrawable$Orientation; 366
+Landroid/graphics/drawable/GradientDrawable$Orientation;.TOP_BOTTOM:Landroid/graphics/drawable/GradientDrawable$Orientation; 366
+Landroid/view/View$$ExternalSyntheticLambda0; 366
+Landroid/graphics/drawable/GradientDrawable$Orientation;.LEFT_RIGHT:Landroid/graphics/drawable/GradientDrawable$Orientation; 366
+Lcom/android/internal/policy/PhoneWindow$1; 366
+Landroid/graphics/drawable/GradientDrawable$Orientation;.BL_TR:Landroid/graphics/drawable/GradientDrawable$Orientation; 366
+Landroid/graphics/drawable/GradientDrawable$Orientation;.TR_BL:Landroid/graphics/drawable/GradientDrawable$Orientation; 366
+Landroid/os/PowerManager$3$$ExternalSyntheticLambda0; 366
+Landroid/hardware/SensorManager; 366
+Landroid/app/SharedPreferencesImpl$EditorImpl$$ExternalSyntheticLambda0; 367
+Landroid/view/View$ScrollabilityCache; 368
+Landroid/graphics/drawable/LevelListDrawable; 368
+Landroid/app/INotificationManager; 368
+Landroid/os/IInterface; 371
+Landroid/app/servertransaction/TopResumedActivityChangeItem; 372
+Landroid/app/servertransaction/ClientTransaction; 373
+Landroid/database/CursorToBulkCursorAdaptor; 375
+Landroid/telephony/TelephonyRegistryManager;.sCarrierPrivilegeCallbacks:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry; 375
+Landroid/net/MatchAllNetworkSpecifier; 375
+Lcom/android/internal/telephony/TelephonyPermissions;.sReportedDeviceIDPackages:Ljava/util/Map; 375
+Landroid/telephony/AnomalyReporter; 375
+Landroid/telephony/NetworkRegistrationInfo; 375
+Landroid/telephony/VoiceSpecificRegistrationInfo; 375
+Landroid/telephony/TelephonyRegistryManager;.sCarrierPrivilegeCallbacks:Ljava/util/WeakHashMap; 375
+Landroid/content/ContentProvider$Transport; 375
+Landroid/app/PropertyInvalidatedCache;.sInvalidates:Ljava/util/HashMap; 376
+Landroid/app/PropertyInvalidatedCache$NoPreloadHolder; 376
+Landroid/app/PropertyInvalidatedCache;.sDisabledKeys:Ljava/util/HashSet;.map:Ljava/util/HashMap; 377
+Landroid/media/session/MediaSessionManager$OnMediaKeyEventSessionChangedListener; 378
+Landroid/app/ActivityManager$MyUidObserver; 378
+Landroid/media/session/MediaSessionManager$SessionsChangedWrapper$1; 378
+Landroid/app/PendingIntent$CancelListener; 379
+Lcom/android/internal/widget/MessagingLayout; 380
+Lcom/android/internal/widget/ImageFloatingTextView; 380
+Landroid/widget/ProgressBar$RefreshData;.sPool:Landroid/util/Pools$SynchronizedPool; 380
+Landroid/widget/ViewSwitcher;.dexCache:Ljava/lang/Object; 380
+Lcom/android/internal/util/PerfettoTrigger;.sLastInvocationPerTrigger:Landroid/util/SparseLongArray;.mKeys:[I 380
+Landroid/widget/DateTimeView$ReceiverInfo$1; 380
+Landroid/widget/SeekBar; 380
+Lcom/android/internal/colorextraction/ColorExtractor$OnColorsChangedListener; 380
+Landroid/view/RemotableViewMethod; 380
+Landroid/view/ViewGroup$ViewLocationHolder;.sPool:Landroid/util/Pools$SynchronizedPool; 380
+Landroid/view/View;.TRANSLATION_Y:Landroid/util/Property; 380
+Lcom/android/internal/widget/ConversationLayout; 380
+Lcom/android/internal/util/ContrastColorUtil; 380
+Lcom/android/internal/widget/ImageResolver; 380
+Landroid/view/View;.SCALE_X:Landroid/util/Property; 380
+Landroid/view/ViewGroup$ViewLocationHolder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 380
+Landroid/permission/PermissionManager;.INDICATOR_EXEMPTED_PACKAGES:[Ljava/lang/String; 380
+Landroid/media/MediaRouter2Manager; 380
+Landroid/hardware/display/NightDisplayListener$Callback; 380
+Lcom/android/internal/widget/NotificationOptimizedLinearLayout; 380
+Landroid/hardware/biometrics/BiometricSourceType;.IRIS:Landroid/hardware/biometrics/BiometricSourceType; 380
+Landroid/view/NotificationTopLineView; 380
+Landroid/os/HandlerExecutor; 380
+Lcom/android/internal/widget/RemeasuringLinearLayout; 380
+Lcom/android/internal/util/PerfettoTrigger;.sLastInvocationPerTrigger:Landroid/util/SparseLongArray; 380
+Landroid/animation/ValueAnimator$DurationScaleChangeListener; 380
+Landroid/view/ViewGroup$ChildListForAccessibility;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 380
+Landroid/widget/ProgressBar$RefreshData;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 380
+Landroid/graphics/drawable/DrawableInflater;.CONSTRUCTOR_MAP:Ljava/util/HashMap; 380
+Landroid/transition/TransitionManager;.sPendingTransitions:Ljava/util/ArrayList; 380
+Landroid/view/SurfaceControl; 380
+Lcom/android/internal/widget/CachingIconView; 380
+Landroid/view/animation/AnimationSet; 380
+Landroid/hardware/face/FaceManager$FaceDetectionCallback; 380
+Lcom/android/internal/widget/NotificationExpandButton; 380
+Landroid/view/View;.SCALE_Y:Landroid/util/Property; 380
+Landroid/view/NotificationHeaderView; 380
+Landroid/widget/RemoteViews;.sMethods:Landroid/util/ArrayMap; 380
+Landroid/widget/DateTimeView; 380
+Lcom/android/internal/widget/NotificationActionListLayout; 380
+Landroid/view/accessibility/AccessibilityManager$$ExternalSyntheticLambda0; 380
+Landroid/view/ViewOverlay$OverlayViewGroup; 380
+Landroid/text/TextShaper$GlyphsConsumer; 380
+Landroid/permission/PermissionManager; 380
+Landroid/widget/RemoteViews;.sLookupKey:Landroid/widget/RemoteViews$MethodKey; 380
+Lcom/android/internal/logging/UiEventLogger; 380
+Landroid/hardware/biometrics/BiometricSourceType;.FACE:Landroid/hardware/biometrics/BiometricSourceType; 380
+Lcom/android/internal/view/menu/ActionMenuItemView; 380
+Landroid/hardware/biometrics/BiometricSourceType;.FINGERPRINT:Landroid/hardware/biometrics/BiometricSourceType; 380
+Lcom/android/internal/util/PerfettoTrigger;.sLastInvocationPerTrigger:Landroid/util/SparseLongArray;.mValues:[J 380
+Landroid/app/trust/TrustManager$TrustListener; 380
+Landroid/view/ViewGroup$ChildListForAccessibility;.sPool:Landroid/util/Pools$SynchronizedPool; 380
+Landroid/view/View$OnHoverListener; 381
+Landroid/content/res/ColorStateList; 381
+Landroid/view/inputmethod/EditorInfo; 382
+Landroid/view/Window$DecorCallback; 382
+Landroid/view/MenuItem$OnActionExpandListener; 382
+Lcom/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry; 382
+Landroid/widget/inline/InlinePresentationSpec; 383
+Landroid/os/DeadObjectException; 384
+Landroid/content/ContentProviderClient; 385
+Landroid/os/UserHandle;.sExtraUserHandleCache:Landroid/util/SparseArray; 385
+Landroid/util/Log$TerribleFailure; 386
+Landroid/telephony/DataSpecificRegistrationInfo; 387
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle; 388
+Landroid/telephony/NetworkService; 389
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerEvent; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteController; 390
+Lcom/android/internal/telephony/GsmCdmaCallTracker; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.483:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularServiceState; 390
+Lcom/android/ims/rcs/uce/eab/EabProvider; 390
+Lcom/android/internal/telephony/NetworkTypeController$DefaultState; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierRoamingSatelliteSession; 390
+Lcom/android/internal/telephony/ProxyController; 390
+Lcom/android/internal/telephony/TelephonyComponentFactory; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSosMessageRecommender; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationTermination; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.23:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/DisplayInfoController; 390
+Lcom/android/internal/telephony/DeviceStateMonitor$3; 390
+Landroid/telephony/CellSignalStrengthWcdma; 390
+Landroid/os/Handler$MessengerImpl; 390
+Lcom/android/internal/telephony/TelephonyDevController;.mModems:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/CarrierServiceBindHelper$CarrierServicePackageMonitor; 390
+Landroid/telephony/ims/RegistrationManager$RegistrationCallback$RegistrationBinder; 390
+Lcom/android/internal/telephony/uicc/UiccProfile; 390
+Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler$CdmaScpTestBroadcastReceiver; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.363:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierIdMismatch; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$UnavailableState; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteProvision; 390
+Lcom/android/internal/telephony/emergency/EmergencyNumberTracker$1; 390
+Lcom/android/internal/telephony/metrics/TelephonyMetrics; 390
+Lcom/android/internal/telephony/RILRequest; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$GbaEvent; 390
+Lcom/android/internal/telephony/TelephonyDevController; 390
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker$2; 390
+Lcom/android/internal/telephony/satellite/PointingAppController; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipMessageResponse; 390
+Lcom/android/internal/telephony/InboundSmsHandler$NewMessageNotificationActionReceiver; 390
+Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.831:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/DeviceStateMonitor; 390
+Landroid/telephony/TelephonyRegistryManager$3; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSession; 390
+Landroid/telephony/data/ApnSetting;.APN_TYPE_INT_MAP:Ljava/util/Map; 390
+Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mInCallVoicePrivacyOffRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 390
+Landroid/net/NetworkPolicyManager$SubscriptionCallbackProxy; 390
+Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler; 390
+Lcom/android/internal/telephony/IntentBroadcaster; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.469:[Ljava/lang/String; 390
+Landroid/telephony/BarringInfo$BarringServiceInfo; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.673:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/PackageChangeReceiver; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState$14; 390
+Lcom/android/internal/telephony/RadioInterfaceCapabilityController; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallSession; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationStats; 390
+Lcom/android/internal/telephony/SmsStorageMonitor$1; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequestsV2; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.349:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierRoamingSatelliteControllerStats; 390
+Lcom/android/internal/telephony/security/NullCipherNotifier; 390
+Lcom/android/internal/telephony/uicc/UiccPkcs15$Pkcs15Selector; 390
+Lcom/android/internal/telephony/SMSDispatcher$1; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$DataNetworkValidation; 390
+Lcom/android/internal/telephony/CarrierActionAgent; 390
+Lcom/android/internal/telephony/TelephonyTester$1; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportSession; 390
+Lcom/android/internal/telephony/IntentBroadcaster$1; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mTtyModeReceivedRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mMmiCompleteRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mSuppServiceFailedRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$PresenceNotifyEvent; 390
+Lcom/android/i18n/timezone/TimeZoneFinder; 390
+Lcom/android/internal/telephony/SimActivationTracker$1; 390
+Lcom/android/internal/telephony/NitzStateMachine; 390
+Lcom/android/internal/telephony/ims/ImsResolver$3; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$DisconnectedState; 390
+Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1457:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler$GsmCbTestBroadcastReceiver; 390
+Landroid/timezone/TelephonyLookup; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerListenerEvent; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$IncomingSms; 390
+Lcom/android/internal/telephony/UiccPhoneBookController; 390
+Lcom/android/internal/telephony/imsphone/ImsPhone; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$AvailableState; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$BindingState; 390
+Lcom/android/internal/telephony/euicc/EuiccCardController; 390
+Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall; 390
+Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mDisplayInfoRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/TelephonyDevController;.mSims:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/AppSmsManager; 390
+Lcom/android/internal/telephony/SmsUsageMonitor; 390
+Lcom/android/internal/telephony/cat/CatService; 390
+Lcom/android/internal/telephony/GsmCdmaCallTracker$1; 390
+Landroid/telephony/ims/ProvisioningManager$Callback$CallbackBinder; 390
+Landroid/telephony/ims/ImsMmTelManager$CapabilityCallback$CapabilityBinder; 390
+Landroid/telephony/CellSignalStrengthGsm; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$EmergencyNumbersInfo; 390
+Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler$CdmaCbTestBroadcastReceiver; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1441:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/RIL;.sRilTimeHistograms:Landroid/util/SparseArray; 390
+Landroid/app/timezonedetector/TimeZoneDetector; 390
+Lcom/android/internal/telephony/SmsBroadcastUndelivered; 390
+Landroid/telephony/emergency/EmergencyNumber; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallRatUsage; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularDataServiceSwitch; 390
+Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 390
+Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray;.mKeys:[I 390
+Lcom/android/internal/telephony/CommandException$Error;.INVALID_SIM_STATE:Lcom/android/internal/telephony/CommandException$Error; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mDisconnectRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/PhoneSubInfoController; 390
+Lcom/android/internal/telephony/PhoneFactory; 390
+Lcom/android/internal/telephony/SmsApplication$SmsPackageMonitor; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportFeatureTagStats; 390
+Landroid/telephony/ims/aidl/IImsServiceController$Stub$Proxy; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsAcsProvisioningStats; 390
+Lcom/android/internal/telephony/SmsStorageMonitor; 390
+Lcom/android/internal/telephony/uicc/UiccController; 390
+Lcom/android/internal/telephony/GsmCdmaPhone; 390
+Landroid/telephony/CellSignalStrengthTdscdma; 390
+Lcom/android/internal/telephony/SomeArgs; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mHandlerMap:Ljava/util/HashMap; 390
+Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager; 390
+Lcom/android/internal/telephony/uicc/UiccProfile$2; 390
+Lcom/android/internal/telephony/LocaleTracker; 390
+Lcom/android/internal/telephony/CarrierKeyDownloadManager$3; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$1; 390
+Lcom/android/internal/telephony/CarrierServiceBindHelper$1; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mBackgroundCalls:Ljava/util/ArrayList; 390
+Landroid/telephony/CellSignalStrengthCdma; 390
+Landroid/telephony/TelephonyLocalConnection; 390
+Lcom/android/internal/telephony/RilWakelockInfo; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mForegroundCalls:Ljava/util/ArrayList; 390
+Landroid/os/AsyncResult; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteIncomingDatagram; 390
+Lcom/android/i18n/timezone/TelephonyLookup; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.789:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/euicc/EuiccController; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingSms; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.33:[Ljava/lang/String; 390
+Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.549:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingShortCodeSms; 390
+Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo; 390
+Lcom/android/internal/telephony/IWapPushManager; 390
+Lcom/android/internal/telephony/MccTable; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipDelegateStats; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$UnmeteredNetworks; 390
+Lcom/android/internal/telephony/IccSmsInterfaceManager; 390
+Lcom/android/internal/telephony/PhoneConfigurationManager; 390
+Lcom/android/internal/telephony/ims/ImsServiceController$ImsFeatureStatusCallback$1; 390
+Lcom/android/internal/telephony/uicc/UiccProfile$4; 390
+Lcom/android/internal/telephony/euicc/EuiccCardController$SimSlotStatusChangedBroadcastReceiver; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState; 390
+Lcom/android/internal/telephony/emergency/EmergencyNumberTracker; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteConfigUpdater; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsClientProvisioningStats; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$UceEventStats; 390
+Lcom/android/internal/telephony/ims/ImsResolver$2; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mSignalInfoRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/RadioConfig; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mRingingCalls:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/ims/ImsResolver; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$EuiccPackageMonitor; 390
+Lcom/android/internal/telephony/SmsBroadcastUndelivered$1; 390
+Lcom/android/internal/telephony/SmsApplication; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteOutgoingDatagram; 390
+Lcom/android/internal/telephony/uicc/PinStorage$1; 390
+Lcom/android/internal/telephony/IccPhoneBookInterfaceManager; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequests; 390
+Lcom/android/internal/telephony/ServiceStateTracker; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationServiceDescStats; 390
+Lcom/android/internal/telephony/ServiceStateTracker$1; 390
+Landroid/net/TelephonyNetworkSpecifier; 390
+Landroid/telephony/CellSignalStrengthNr; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mInCallVoicePrivacyOnRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 390
+Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mPhones:Ljava/util/ArrayList; 390
+Landroid/telephony/ModemActivityInfo; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationFeatureTagStats; 390
+Lcom/android/internal/telephony/LocaleTracker$1; 390
+Lcom/android/internal/telephony/SmsDispatchersController; 390
+Landroid/telephony/ModemInfo; 390
+Lcom/android/internal/telephony/CommandException; 390
+Lcom/android/internal/telephony/satellite/SatelliteModemInterface; 390
+Lcom/android/internal/telephony/CarrierPrivilegesTracker$1; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$DataCallSession; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1171:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/SimActivationTracker; 390
+Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyServiceState$NetworkRegistrationInfo; 390
+Landroid/telephony/CellSignalStrengthLte; 390
+Lcom/android/internal/telephony/CarrierActionAgent$1; 390
+Landroid/timezone/TimeZoneFinder; 390
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1287:[Ljava/lang/String; 390
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteEntitlement; 390
+Lcom/android/internal/telephony/CarrierResolver$2; 390
+Lcom/android/internal/telephony/CellBroadcastServiceManager; 390
+Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState$5; 390
+Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker; 390
+Lcom/android/internal/telephony/uicc/UiccCarrierPrivilegeRules; 390
+Lcom/android/internal/telephony/NetworkTypeController$1; 390
+Lcom/android/internal/telephony/util/NotificationChannelController$1; 390
+Lcom/android/internal/telephony/StateMachine$SmHandler; 390
+Lcom/android/ims/FeatureConnector$1; 390
+Lcom/android/internal/telephony/ims/ImsResolver$1; 390
+Lcom/android/internal/telephony/MultiSimSettingController; 390
+Landroid/telephony/ims/aidl/IImsConfig$Stub$Proxy; 391
+Landroid/telephony/ims/aidl/IImsRegistration$Stub$Proxy; 391
+Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray; 392
+Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 392
+Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray;.mKeys:[I 393
+Landroid/telephony/ims/ImsUtListener; 394
+Landroid/telephony/ims/feature/MmTelFeature$1; 394
+Landroid/telephony/ims/stub/ImsRegistrationImplBase$1; 394
+Landroid/telephony/ims/stub/ImsConfigImplBase$ImsConfigStub; 394
+Landroid/telephony/TelephonyRegistryManager; 396
+Landroid/widget/Button; 397
+Landroid/widget/TextView; 398
+Lcom/android/internal/telephony/MccTable;.FALLBACKS:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.6:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 399
+Landroid/media/AudioManager$OnAudioFocusChangeListener; 400
+primitive [I 401
+Landroid/os/Parcelable$Creator; 402
+Landroid/os/Bundle; 403
+Landroid/graphics/Bitmap$Config;.HARDWARE:Landroid/graphics/Bitmap$Config; 404
+Landroid/graphics/Bitmap$Config;.RGBA_F16:Landroid/graphics/Bitmap$Config; 404
+Landroid/renderscript/Allocation;.mBitmapOptions:Landroid/graphics/BitmapFactory$Options;.inPreferredConfig:Landroid/graphics/Bitmap$Config; 404
+Landroid/graphics/Bitmap$Config;.ALPHA_8:Landroid/graphics/Bitmap$Config; 404
+Landroid/graphics/Bitmap$Config;.RGBA_1010102:Landroid/graphics/Bitmap$Config; 404
+Landroid/graphics/Bitmap$Config;.ARGB_4444:Landroid/graphics/Bitmap$Config; 404
+Landroid/graphics/Bitmap$Config;.RGB_565:Landroid/graphics/Bitmap$Config; 404
+Landroid/icu/util/Calendar;.WEEK_DATA_CACHE:Landroid/icu/util/Calendar$WeekDataCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 407
+Landroid/icu/util/ULocale; 408
+Landroid/graphics/Paint;.sMinikinLocaleListIdCache:Ljava/util/HashMap; 409
+Landroid/icu/text/BreakIterator;.iterCache:[Landroid/icu/impl/CacheValue; 410
+Landroid/widget/EditText; 411
+Landroid/view/autofill/AutofillValue; 412
+Landroid/view/ViewGroup$ChildListForAutoFillOrContentCapture;.sPool:Landroid/util/Pools$SimplePool;.mPool:[Ljava/lang/Object; 413
+Landroid/view/ViewGroup$ChildListForAutoFillOrContentCapture;.sPool:Landroid/util/Pools$SimplePool; 413
+Landroid/view/ViewGroup; 414
+Landroid/graphics/Rect; 415
+Landroid/graphics/Insets; 416
+Landroid/content/LocusId; 417
+Landroid/view/contentcapture/ContentCaptureContext; 417
+Landroid/telephony/TelephonyCallback$DisplayInfoListener; 419
+Landroid/app/assist/AssistStructure$HtmlInfoNode; 420
+Landroid/app/ActivityManager$AppTask; 420
+Landroid/database/CursorIndexOutOfBoundsException; 422
+Landroid/media/MediaPlayer; 422
+Lcom/android/internal/policy/DecorView$2; 423
+Landroid/security/keystore2/AndroidKeyStoreRSAPrivateKey; 424
+Landroid/widget/Spinner; 424
+Landroid/app/slice/Slice;.SUBTYPE_SOURCE:Ljava/lang/String; 425
+Ljavax/sip/message/Request;.INFO:Ljava/lang/String; 425
+Lgov/nist/javax/sip/header/AuthenticationHeader;.SIGNATURE:Ljava/lang/String; 426
+Landroid/widget/RadioGroup$OnCheckedChangeListener; 428
+Ljavax/sip/header/AcceptEncodingHeader;.NAME:Ljava/lang/String; 429
+Landroid/app/ReceiverRestrictedContext; 429
+Landroid/content/Context;.ACCOUNT_SERVICE:Ljava/lang/String; 429
+Landroid/service/trust/TrustAgentService;.EXTRA_TOKEN:Ljava/lang/String; 429
+Landroid/view/VelocityTracker;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 429
+Lcom/android/internal/widget/DialogTitle; 429
+Landroid/view/VelocityTracker;.sPool:Landroid/util/Pools$SynchronizedPool; 429
+Landroid/os/StrictMode$OnThreadViolationListener; 429
+Lcom/android/internal/widget/ButtonBarLayout; 429
+Lcom/android/internal/widget/AlertDialogLayout; 429
+Lcom/android/internal/logging/AndroidHandler; 430
+Landroid/webkit/JavascriptInterface; 446
+Landroid/icu/impl/StandardPlural; 447
+Landroid/icu/impl/number/range/StandardPluralRanges; 448
+Landroid/icu/text/PluralRules$Operand; 448
+Landroid/icu/impl/PluralRulesLoader;.loader:Landroid/icu/impl/PluralRulesLoader; 448
+Landroid/icu/impl/PluralRulesLoader;.loader:Landroid/icu/impl/PluralRulesLoader;.pluralRulesCache:Ljava/util/Map; 448
+Landroid/webkit/CookieManager; 449
+Landroid/os/strictmode/Violation; 459
+Lcom/android/internal/telephony/uicc/asn1/Asn1Node;.EMPTY_NODE_LIST:Ljava/util/List; 460
+Landroid/os/strictmode/CredentialProtectedWhileLockedViolation; 462
+Landroid/content/AsyncQueryHandler; 463
+Landroid/widget/CheckedTextView; 464
+Landroid/app/Activity$$ExternalSyntheticLambda0; 464
+Landroid/app/job/JobParameters; 467
+Landroid/os/SystemProperties; 468
+Landroid/hardware/display/DisplayManager$DisplayListener; 470
+Landroid/view/View$OnApplyWindowInsetsListener; 470
+Landroid/view/Choreographer$FrameCallback; 471
+Landroid/os/Handler$Callback; 472
+Landroid/telephony/TelephonyCallback$DataConnectionStateListener; 473
+Landroid/view/WindowManagerImpl; 474
+Landroid/util/DisplayMetrics; 474
+Landroid/content/ServiceConnection; 474
+Landroid/app/ActivityManager$MemoryInfo; 474
+Landroid/view/Display; 474
+Landroid/os/VibrationEffect; 475
+Landroid/view/SurfaceView; 476
+Landroid/content/ContextWrapper; 477
+Landroid/app/Application; 478
+Landroid/view/View$OnSystemUiVisibilityChangeListener; 479
+Landroid/view/View$OnLayoutChangeListener; 480
+Landroid/os/Build$VERSION; 481
+Landroid/view/InputDevice; 482
+Landroid/preference/PreferenceManager; 482
+Landroid/app/SharedPreferencesImpl$EditorImpl; 482
+Landroid/widget/Switch; 486
+Landroid/view/View$OnGenericMotionListener; 487
+Landroid/icu/text/DecimalFormatSymbols;.DEF_DIGIT_STRINGS_ARRAY:[Ljava/lang/String;.1:Ljava/lang/String; 488
+Landroid/widget/Editor$TextRenderNode; 489
+Landroid/database/sqlite/SQLiteCantOpenDatabaseException; 490
+Landroid/accounts/Account; 490
+Landroid/accounts/Account;.sAccessedAccounts:Ljava/util/Set; 491
+Landroid/app/PendingIntent$OnFinished; 492
+Landroid/hardware/location/ContextHubTransaction$OnCompleteListener; 492
+Landroid/os/WorkSource; 492
+Landroid/os/ParcelUuid; 493
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.12:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mLock:Ljava/lang/Object; 493
+Landroid/database/sqlite/SQLiteConstraintException; 493
+Landroid/location/Location; 493
+Landroid/os/strictmode/CustomViolation; 494
+Landroid/graphics/drawable/PictureDrawable; 495
+primitive [C 504
+primitive [S 505
+Landroid/view/AttachedSurfaceControl$OnBufferTransformHintChangedListener; 507
+Landroid/webkit/WebChromeClient$CustomViewCallback; 508
+primitive [B 514
+Landroid/view/Window$OnFrameMetricsAvailableListener; 516
+Landroid/net/Uri$PathPart;.EMPTY:Landroid/net/Uri$PathPart; 521
+Landroid/app/IActivityManager; 522
+Landroid/text/style/ImageSpan; 523
+Landroid/security/keystore2/AndroidKeyStoreECPrivateKey; 524
+Landroid/security/keystore/KeyInfo; 525
+Landroid/view/WindowLeaked; 526
+Landroid/app/ContentProviderHolder; 526
+Landroid/text/style/URLSpan; 526
+Landroid/icu/util/ULocale$AvailableType;.DEFAULT:Landroid/icu/util/ULocale$AvailableType;.name:Ljava/lang/String; 527
+Lcom/android/internal/telephony/cdnr/CarrierDisplayNameResolver;.EF_SOURCE_PRIORITY:Ljava/util/List;.a:[Ljava/lang/Object;.9:Ljava/lang/Integer; 528
+Lcom/android/internal/telephony/WspTypeDecoder;.WELL_KNOWN_MIME_TYPES:Ljava/util/HashMap;.table:[Ljava/util/HashMap$Node;.11:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 528
+Lcom/android/internal/telephony/cdnr/CarrierDisplayNameResolver;.EF_SOURCE_PRIORITY:Ljava/util/List;.a:[Ljava/lang/Object;.2:Ljava/lang/Integer; 529
+Landroid/text/Html$TagHandler; 530
+Landroid/text/HtmlToSpannedConverter$Font; 531
+Landroid/transition/Explode; 532
+Landroid/content/pm/PackageManager$OnPermissionsChangedListener; 535
+Landroid/os/UserManager; 537
+Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal; 538
+Landroid/graphics/ColorSpace$Model;.RGB:Landroid/graphics/ColorSpace$Model; 539
+Landroid/app/WallpaperManager; 539
+Landroid/app/Notification; 540
+Landroid/app/RemoteAction; 541
+Landroid/database/sqlite/SQLiteTransactionListener; 542
+Landroid/accounts/OnAccountsUpdateListener; 544
+Landroid/accounts/AccountManager$20; 545
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mLock:Ljava/lang/Object; 548
+Landroid/hardware/SensorEventListener; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.6:Ljava/lang/String; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.237:Ljava/lang/String; 553
+Lcom/android/internal/content/om/OverlayConfigParser;.CONFIG_DIRECTORY:Ljava/lang/String; 553
+Lcom/android/internal/app/procstats/DumpUtils;.STATE_TAGS:[Ljava/lang/String;.14:Ljava/lang/String; 553
+Landroid/opengl/GLSurfaceView; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.127:Ljava/lang/String; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.141:Ljava/lang/String; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.80:Ljava/lang/String; 553
+Landroid/util/AndroidRuntimeException; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.35:Ljava/lang/String; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.15:Ljava/lang/String; 553
+Landroid/text/Html$ImageGetter; 553
+Landroid/view/ThreadedRenderer;.OVERDRAW_PROPERTY_SHOW:Ljava/lang/String; 553
+Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.9:Ljava/lang/String; 553
+Landroid/window/ImeOnBackInvokedDispatcher;.RESULT_KEY_PRIORITY:Ljava/lang/String; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.118:Ljava/lang/String; 553
+Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.221:Ljava/lang/String; 553
+Landroid/transition/TransitionInflater;.sConstructors:Landroid/util/ArrayMap; 554
+Lcom/android/internal/transition/EpicenterTranslateClipReveal; 554
+Landroid/widget/HorizontalScrollView$SavedState; 555
+Landroid/widget/Spinner$SavedState; 555
+Landroid/widget/AbsSpinner$SavedState; 555
+Lcom/android/internal/telephony/WspTypeDecoder;.WELL_KNOWN_MIME_TYPES:Ljava/util/HashMap;.table:[Ljava/util/HashMap$Node;.33:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 557
+Landroid/graphics/drawable/ShapeDrawable; 558
+Landroid/text/method/DialerKeyListener; 560
+Lgov/nist/javax/sip/address/NetObject;.PHONE:Ljava/lang/String; 567
+Landroid/app/usage/UsageEvents$Event;.DEVICE_EVENT_PACKAGE_NAME:Ljava/lang/String; 567
+Landroid/view/translation/UiTranslationManager;.EXTRA_PACKAGE_NAME:Ljava/lang/String; 567
+Landroid/icu/text/MessageFormat;.dateModifierList:[Ljava/lang/String;.3:Ljava/lang/String; 567
+Landroid/icu/impl/ValidIdentifiers$Datatype;.language:Landroid/icu/impl/ValidIdentifiers$Datatype;.name:Ljava/lang/String; 567
+Lgov/nist/javax/sip/header/extensions/ReferencesHeader;.SERVICE:Ljava/lang/String; 567
+Landroid/icu/impl/locale/LocaleValidityChecker$SpecialCase;.normal:Landroid/icu/impl/locale/LocaleValidityChecker$SpecialCase;.name:Ljava/lang/String; 567
+Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.140:Ljava/lang/String; 568
+Landroid/icu/impl/units/UnitPreferences;.measurementSystem:Ljava/util/Map;.m:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.15:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 569
+Lcom/android/i18n/timezone/TimeZoneFinder;.COUNTRY_ELEMENT:Ljava/lang/String; 570
+Landroid/icu/text/MessageFormat;.rootLocale:Ljava/util/Locale;.baseLocale:Lsun/util/locale/BaseLocale;.language:Ljava/lang/String; 571
+Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener; 585
+Landroid/graphics/drawable/AnimatedVectorDrawable; 590
+Landroid/view/TextureView; 592
+Landroid/app/Notification$MessagingStyle; 596
+Landroid/os/Message;.sPoolSync:Ljava/lang/Object; 596
+Landroid/os/strictmode/LeakedClosableViolation; 597
+Landroid/view/DisplayCutout; 597
+Landroid/app/AppOpsManager$OnOpNotedListener; 597
+Lcom/android/internal/policy/AttributeCache; 597
+Landroid/app/Notification$CallStyle; 597
+Lcom/android/internal/logging/UiEventLogger$UiEventEnum; 597
+Lcom/android/internal/statusbar/NotificationVisibility; 597
+Landroid/app/AppOpsManager$OnOpNotedInternalListener; 597
+Landroid/window/IRemoteTransition$Stub$Proxy; 597
+Lcom/android/internal/logging/MetricsLogger; 597
+Landroid/hardware/display/DisplayManagerGlobal$DisplayListenerDelegate$$ExternalSyntheticLambda0; 597
+Landroid/app/Notification$DecoratedCustomViewStyle; 597
+Landroid/app/AppOpsManager$OnOpStartedListener; 597
+Lcom/android/internal/R$styleable;.WindowAnimation:[I 597
+Lcom/android/internal/util/LatencyTracker$Action; 597
+Landroid/app/Notification$BigPictureStyle; 598
+Landroid/app/Notification$MediaStyle; 598
+Landroid/app/Notification$InboxStyle; 598
+Landroid/app/Notification$BigTextStyle; 599
+Landroid/app/AppOpsManager$Mode; 600
+Landroid/view/accessibility/AccessibilityManager$AccessibilityServicesStateChangeListener; 601
+Landroid/annotation/IdRes; 602
+Landroid/app/usage/AppStandbyInfo; 603
+Landroid/content/ContentProvider$PipeDataWriter; 604
+Landroid/os/strictmode/UnsafeIntentLaunchViolation; 604
+Landroid/app/servertransaction/ObjectPool;.sPoolMap:Ljava/util/Map; 605
+Landroid/app/servertransaction/ResumeActivityItem; 605
+Landroid/app/servertransaction/ActivityRelaunchItem; 605
+Landroid/os/strictmode/DiskReadViolation; 606
+Landroid/net/metrics/DhcpClientEvent; 607
+Landroid/icu/impl/CharacterPropertiesImpl;.inclusions:[Landroid/icu/text/UnicodeSet; 608
+Lcom/android/internal/policy/PhoneWindow; 616
+Landroid/os/ResultReceiver$MyRunnable; 619
+Landroid/window/ImeOnBackInvokedDispatcher$$ExternalSyntheticLambda0; 620
+Landroid/text/StaticLayout; 620
+Landroid/graphics/SurfaceTexture$1; 620
+Landroid/animation/AnimationHandler$AnimationFrameCallbackProvider; 620
+Landroid/provider/FontsContract;.sTypefaceCache:Landroid/util/LruCache; 622
+Landroid/provider/FontsContract;.sTypefaceCache:Landroid/util/LruCache;.map:Ljava/util/LinkedHashMap; 623
+Landroid/graphics/drawable/InsetDrawable; 624
+Landroid/graphics/PorterDuff$Mode;.SRC_IN:Landroid/graphics/PorterDuff$Mode; 625
+Landroid/widget/CheckBox; 626
+Landroid/widget/RadioButton; 627
+Landroid/content/pm/PackageManager; 628
+Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader;.packages:Ljava/util/Map;.m:Ljava/util/Map;.table:[Ljava/util/HashMap$Node; 631
+Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader;.packages:Ljava/util/Map;.m:Ljava/util/Map; 631
+Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader; 631
+Landroid/content/AttributionSource; 632
+Landroid/widget/GridLayout;.UNDEFINED_ALIGNMENT:Landroid/widget/GridLayout$Alignment; 633
+Landroid/text/Spanned; 636
+Landroid/renderscript/RenderScript; 636
+Lcom/android/internal/policy/PhoneLayoutInflater; 636
+Landroid/content/pm/IPackageManager$Stub$Proxy; 637
+Landroid/widget/ViewSwitcher; 638
+Landroid/app/IActivityTaskManager$Stub$Proxy; 639
+Lcom/android/internal/telephony/ITelephony; 639
+Landroid/content/IntentFilter; 640
+Landroid/telephony/TelephonyCallback$RadioPowerStateListener; 641
+Landroid/telephony/TelephonyCallback$ServiceStateListener; 642
+Landroid/app/compat/CompatChanges;.QUERY_CACHE:Landroid/app/compat/ChangeIdStateCache;.mCache:Ljava/util/LinkedHashMap; 643
+Landroid/app/compat/CompatChanges;.QUERY_CACHE:Landroid/app/compat/ChangeIdStateCache; 643
+Landroid/app/AlarmManager; 644
+Landroid/text/format/DateFormat; 647
+Landroid/icu/text/Collator; 648
+Landroid/widget/TextView$SavedState; 649
+Landroid/widget/ViewFlipper; 661
+Landroid/os/PersistableBundle; 664
+Landroid/content/pm/ShortcutInfo; 665
+Landroid/graphics/drawable/Icon; 666
+Landroid/content/Intent; 667
+Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.197:Landroid/os/PersistableBundle; 668
+Landroid/app/ActivityTaskManager;.sInstance:Landroid/util/Singleton; 672
+Landroid/widget/Space; 677
+Landroid/content/pm/ApplicationInfo; 678
+primitive [J 681
+primitive [D 682
+primitive [Z 683
+primitive [F 684
+Landroid/window/SplashScreen; 685
+Landroid/app/servertransaction/LaunchActivityItem; 689
+Landroid/graphics/Matrix; 691
+Landroid/graphics/RectF; 691
+Landroid/os/Parcel$LazyValue; 691
+Landroid/app/FragmentManagerState; 693
+Landroid/view/AbsSavedState$1; 694
+Lcom/android/internal/policy/PhoneWindow$PanelFeatureState$SavedState; 695
+Landroid/net/Uri; 697
+Landroid/view/View;.sNextGeneratedId:Ljava/util/concurrent/atomic/AtomicInteger; 698
+Landroid/media/MediaRouter$WifiDisplayStatusChangedReceiver; 701
+Landroid/media/MediaRouter$VolumeChangeReceiver; 701
+Landroid/media/MediaRouter; 702
+Landroid/hardware/SensorPrivacyManager; 703
+Landroid/os/storage/StorageManager; 704
+Landroid/credentials/CredentialManager; 705
+Landroid/media/tv/tunerresourcemanager/TunerResourceManager; 705
+Landroid/provider/E2eeContactKeysManager; 705
+Landroid/service/persistentdata/PersistentDataBlockManager; 705
+Landroid/os/HardwarePropertiesManager; 705
+Landroid/app/wearable/WearableSensingManager; 705
+Landroid/net/vcn/VcnManager; 705
+Landroid/app/admin/DevicePolicyManager; 705
+Landroid/app/contentsuggestions/ContentSuggestionsManager; 705
+Landroid/view/textservice/TextServicesManager; 705
+Landroid/view/textclassifier/TextClassificationManager; 705
+Landroid/media/session/MediaSessionManager; 705
+Landroid/view/translation/TranslationManager; 705
+Landroid/view/WindowManager; 705
+Landroid/os/SystemConfigManager; 705
+Landroid/hardware/input/InputManager; 705
+Landroid/permission/PermissionControllerManager; 705
+Landroid/app/people/PeopleManager; 705
+Landroid/app/contextualsearch/ContextualSearchManager; 705
+Landroid/os/RecoverySystem; 705
+Landroid/net/wifi/sharedconnectivity/app/SharedConnectivityManager; 705
+Landroid/security/attestationverification/AttestationVerificationManager; 705
+Landroid/view/autofill/AutofillManager; 705
+Landroid/telephony/SubscriptionManager; 705
+Landroid/view/LayoutInflater; 705
+Landroid/net/NetworkPolicyManager; 705
+Landroid/view/contentcapture/ContentCaptureManager; 705
+Landroid/content/pm/PackageManager$NameNotFoundException; 706
+Landroid/hardware/devicestate/DeviceStateManagerGlobal; 707
+Landroid/telecom/TelecomManager; 708
+Landroid/content/pm/ParceledListSlice; 709
+Landroid/app/NotificationChannelGroup; 709
+Landroid/os/vibrator/StepSegment; 710
+Landroid/security/keystore2/KeyStoreCryptoOperationUtils; 711
+Landroid/security/keystore2/AndroidKeyStoreProvider; 712
+Landroid/widget/ProgressBar; 715
+Landroid/animation/LayoutTransition; 716
+Landroid/widget/ImageButton; 717
+Landroid/graphics/drawable/AdaptiveIconDrawable; 718
+Landroid/widget/ActionMenuView; 719
+Landroid/widget/Toolbar; 719
+Landroid/widget/ActionMenuPresenter$OverflowMenuButton; 719
+Lcom/android/internal/widget/ActionBarContainer; 720
+Lcom/android/internal/widget/ActionBarContainer$ActionBarBackgroundDrawable; 720
+Lcom/android/internal/widget/ActionBarContextView; 720
+Lcom/android/internal/widget/ActionBarOverlayLayout; 720
+Landroid/app/Fragment;.sClassMap:Landroid/util/ArrayMap; 721
+Landroid/graphics/drawable/TransitionDrawable; 722
+Landroid/media/MediaDrm$OnEventListener; 723
+Landroid/widget/MediaController$MediaPlayerControl; 724
+Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PIXEL_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CameraCharacteristics;.INFO_SESSION_CONFIGURATION_QUERY_VERSION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_ACTIVE_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/util/Size; 725
+Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AE_AVAILABLE_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_SHADING_MAP_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CameraCaptureSession$StateCallback; 725
+Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/telephony/ims/ImsService;.CAPABILITIES_LOG_MAP:Ljava/util/Map;.table:[Ljava/lang/Object;.8:Ljava/lang/Long; 725
+Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AF_AVAILABLE_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/view/displayhash/DisplayHashManager; 725
+Landroid/hardware/camera2/CaptureResult;.SENSOR_TIMESTAMP:Landroid/hardware/camera2/CaptureResult$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_EXPOSURE_COMPENSATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AWB_AVAILABLE_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CaptureRequest;.CONTROL_ZOOM_RATIO:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/marshal/MarshalRegistry;.sMarshalLock:Ljava/lang/Object; 725
+Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_TARGET_FPS_RANGE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CaptureRequest;.CONTROL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/usb/UsbManager;.FUNCTION_NAME_TO_CODE:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.13:Ljava/util/HashMap$Node;.next:Ljava/util/HashMap$Node;.next:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 725
+Landroid/hardware/camera2/CaptureRequest;.CONTROL_ENABLE_ZSL:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CameraDevice$StateCallback; 725
+Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_ZOOM_RATIO_RANGE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_PARTIAL_RESULT_COUNT:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 725
+Landroid/hardware/camera2/CameraCaptureSession$CaptureCallback; 725
+Landroid/hardware/camera2/CameraCharacteristics;.FLASH_INFO_AVAILABLE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 726
+Landroid/text/format/DateUtils; 727
+Landroid/security/IKeyChainService; 728
+Landroid/app/usage/UsageStats; 729
+Landroid/app/usage/CacheQuotaHint; 730
+Landroid/service/watchdog/ExplicitHealthCheckService$PackageConfig; 730
+Landroid/os/BaseBundle; 731
+Landroid/view/ViewTreeObserver$OnWindowFocusChangeListener; 732
+Landroid/graphics/ColorMatrix;.dexCache:Ljava/lang/Object; 733
+Landroid/view/InsetsAnimationThread; 736
+Lcom/android/internal/jank/InteractionJankMonitor$InstanceHolder; 737
+Lcom/android/internal/jank/InteractionJankMonitor; 737
+Landroid/graphics/Bitmap;.sAllBitmaps:Ljava/util/WeakHashMap;.queue:Ljava/lang/ref/ReferenceQueue; 738
+Lcom/android/internal/os/BackgroundThread; 739
+Landroid/app/PendingIntent; 740
+Landroid/security/net/config/UserCertificateSource$NoPreloadHolder; 741
+Lorg/apache/http/params/HttpParams; 742
+Landroid/app/Service; 745
+Landroid/content/ComponentCallbacks2; 746
+Landroid/content/ComponentCallbacks; 747
+Landroid/widget/AbsListView; 751
+Landroid/widget/ListView; 752
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry; 753
+Landroid/widget/TextView;.TEMP_RECTF:Landroid/graphics/RectF; 756
+Landroid/app/Activity; 757
+Landroid/text/MeasuredParagraph;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 758
+Landroid/text/MeasuredParagraph;.sPool:Landroid/util/Pools$SynchronizedPool; 758
+Lcom/android/internal/infra/AndroidFuture; 759
+Landroid/widget/TextView;.TEMP_POSITION:[F 760
+Landroid/text/method/MetaKeyKeyListener;.SYM:Ljava/lang/Object; 761
+Landroid/view/inputmethod/SelectRangeGesture; 761
+Landroid/text/method/MetaKeyKeyListener;.CAP:Ljava/lang/Object; 761
+Landroid/text/method/MetaKeyKeyListener;.ALT:Ljava/lang/Object; 761
+Landroid/view/inputmethod/DeleteGesture; 761
+Landroid/view/inputmethod/SelectGesture; 761
+Landroid/text/method/MetaKeyKeyListener;.SELECTING:Ljava/lang/Object; 761
+Landroid/view/inputmethod/DeleteRangeGesture; 761
+Landroid/view/inputmethod/BaseInputConnection;.COMPOSING:Ljava/lang/Object; 762
+Landroid/text/method/DigitsKeyListener;.sLocaleInstanceCache:Ljava/util/HashMap; 763
+Landroid/app/StackTrace; 764
+Landroid/opengl/EGLConfig; 768
+Landroid/widget/PopupWindow$PopupDecorView; 769
+Landroid/widget/PopupWindow$PopupBackgroundView; 769
+Landroid/view/ViewStub$OnInflateListener; 770
+Landroid/opengl/GLSurfaceView;.sGLThreadManager:Landroid/opengl/GLSurfaceView$GLThreadManager; 772
+Landroid/opengl/GLSurfaceView$Renderer; 773
+Landroid/content/ContentValues; 774
+Landroid/graphics/Point; 774
+Landroid/os/Build; 776
+Landroid/widget/ScrollView; 779
+Landroid/os/FileObserver; 780
+Lcom/android/internal/os/RuntimeInit$KillApplicationHandler; 781
+Landroid/app/ResourcesManager; 782
+Landroid/content/res/ResourcesKey; 782
+Landroid/app/backup/BackupManager; 783
+Lcom/android/internal/util/LatencyTracker; 784
+Lcom/android/internal/util/LatencyTracker$SLatencyTrackerHolder; 784
+Landroid/app/Application$ActivityLifecycleCallbacks; 785
+Landroid/os/Messenger; 786
+Landroid/widget/ProgressBar$SavedState; 789
+Lcom/android/internal/telephony/WspTypeDecoder;.WELL_KNOWN_PARAMETERS:Ljava/util/HashMap;.table:[Ljava/util/HashMap$Node;.25:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 792
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.11:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mSkips:[J 793
+Landroid/os/UserHandle; 794
+Landroid/hardware/usb/UsbManager;.FUNCTION_NAME_TO_CODE:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.13:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 796
+Landroid/graphics/drawable/RotateDrawable; 796
+Landroid/database/sqlite/SQLiteException; 797
+Landroid/webkit/WebViewFactory;.sProviderLock:Ljava/lang/Object; 798
+Landroid/content/res/AssetManager$AssetInputStream; 799
+Landroid/util/SparseIntArray; 800
+Landroid/database/ContentObserver; 800
+Landroid/icu/text/NFRule;.ZERO:Ljava/lang/Long; 802
+Landroid/view/View$BaseSavedState; 803
+Landroid/app/ActivityThread$ReceiverData; 804
+Landroid/icu/util/ULocale$AliasReplacer; 805
+Landroid/text/method/TextKeyListener;.ACTIVE:Ljava/lang/Object; 806
+Landroid/text/method/PasswordTransformationMethod; 807
+Landroid/speech/tts/TextToSpeech$Connection$SetupConnectionAsyncTask; 808
+Landroid/speech/tts/TextToSpeech$OnInitListener; 809
+Landroid/icu/util/CodePointMap$RangeOption;.NORMAL:Landroid/icu/util/CodePointMap$RangeOption;.name:Ljava/lang/String; 810
+Lcom/android/internal/telephony/cdnr/CarrierDisplayNameResolver;.EF_SOURCE_PRIORITY:Ljava/util/List;.a:[Ljava/lang/Object;.5:Ljava/lang/Integer; 810
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.5:Ljava/util/WeakHashMap$Entry; 811
+Landroid/window/IWindowContainerToken$Stub$Proxy; 812
+Landroid/telephony/SignalStrength; 813
+Landroid/app/LoadedApk$WarningContextClassLoader; 814
+Landroid/widget/ViewAnimator; 814
+Landroid/util/proto/ProtoStream;.FIELD_TYPE_NAMES:[Ljava/lang/String;.10:Ljava/lang/String; 814
+Lcom/android/internal/telephony/euicc/EuiccController;.EXTRA_OPERATION:Ljava/lang/String; 814
+Lorg/apache/http/conn/ssl/SSLSocketFactory$NoPreloadHolder; 814
+Ljavax/sip/header/PriorityHeader;.NORMAL:Ljava/lang/String; 814
+Landroid/hardware/camera2/CameraCharacteristics;.INFO_DEVICE_STATE_ORIENTATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 816
+Landroid/hardware/camera2/CameraCharacteristics;.INFO_SUPPORTED_HARDWARE_LEVEL:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 816
+Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_ORIENTATION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 816
+Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PHYSICAL_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 817
+Landroid/util/Log; 818
+Landroid/text/style/StyleSpan; 819
+Landroid/security/keystore/KeyGenParameterSpec; 819
+Landroid/telephony/TelephonyCallback$DataEnabledListener; 820
+Landroid/icu/impl/ZoneMeta;.REGION_CACHE:Landroid/icu/impl/ICUCache; 822
+Lcom/android/internal/telephony/WspTypeDecoder;.WELL_KNOWN_MIME_TYPES:Ljava/util/HashMap;.table:[Ljava/util/HashMap$Node;.81:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 823
+Landroid/text/style/QuoteSpan; 824
+Landroid/text/HtmlToSpannedConverter$Strikethrough; 824
+Landroid/text/HtmlToSpannedConverter$Background; 824
+Landroid/text/HtmlToSpannedConverter$Alignment; 824
+Landroid/text/HtmlToSpannedConverter$Foreground; 824
+Landroid/text/method/QwertyKeyListener; 826
+Landroid/graphics/Path$Op; 826
+Landroid/view/OrientationEventListener; 832
+Landroid/animation/PropertyValuesHolder$IntPropertyValuesHolder;.sJNISetterPropertyMap:Ljava/util/HashMap; 834
+Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.11:Ljava/util/WeakHashMap$Entry; 835
+Landroid/view/accessibility/AccessibilityManager;.sInstanceSync:Ljava/lang/Object; 836
+Lcom/android/internal/listeners/ListenerTransport; 837
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 90de7ab..4fb35c3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7312,7 +7312,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void finish(int finishTask) {
if (DEBUG_FINISH_ACTIVITY) {
- Log.d("Instrumentation", "finishActivity: finishTask=" + finishTask, new Throwable());
+ Log.d(Instrumentation.TAG, "finishActivity: finishTask=" + finishTask, new Throwable());
}
if (mParent == null) {
int resultCode;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 74e9583..be70de2 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,7 @@
package android.app;
+import static android.app.Instrumentation.DEBUG_FINISH_ACTIVITY;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -80,6 +81,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.Singleton;
import android.util.Size;
import android.view.WindowInsetsController.Appearance;
@@ -6011,6 +6013,10 @@
* Finishes all activities in this task and removes it from the recent tasks list.
*/
public void finishAndRemoveTask() {
+ if (DEBUG_FINISH_ACTIVITY) {
+ Log.d(Instrumentation.TAG, "AppTask#finishAndRemoveTask: task="
+ + getTaskInfo(), new Throwable());
+ }
try {
mAppTaskImpl.finishAndRemoveTask();
} catch (RemoteException e) {
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index be27046..45852c7 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -98,7 +98,7 @@
*/
public static final String REPORT_KEY_STREAMRESULT = "stream";
- private static final String TAG = "Instrumentation";
+ static final String TAG = "Instrumentation";
private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index fd4d8e9..0cc210b 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1836,9 +1836,10 @@
// have shared library asset paths appended if there are any.
if (r.getImpl() != null) {
final ResourcesImpl oldImpl = r.getImpl();
+ final AssetManager oldAssets = oldImpl.getAssets();
// ResourcesImpl constructor will help to append shared library asset paths.
- if (oldImpl.getAssets().isUpToDate()) {
- final ResourcesImpl newImpl = new ResourcesImpl(oldImpl.getAssets(),
+ if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) {
+ final ResourcesImpl newImpl = new ResourcesImpl(oldAssets,
oldImpl.getMetrics(), oldImpl.getConfiguration(),
oldImpl.getDisplayAdjustments());
r.setImpl(newImpl);
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index c789af3..9148e3c 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -37,7 +37,6 @@
}
}
-
flag {
name: "onboarding_bugreport_v2_enabled"
is_exported: true
@@ -403,3 +402,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "dont_read_policy_definition"
+ namespace: "enterprise"
+ description: "Rely on <policy-key-entry> to determine policy definition and ignore <policy-definition-entry>"
+ bug: "335663055"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/appfunctions/OWNERS b/core/java/android/app/appfunctions/OWNERS
new file mode 100644
index 0000000..c6827cc
--- /dev/null
+++ b/core/java/android/app/appfunctions/OWNERS
@@ -0,0 +1,6 @@
+avayvod@google.com
+oadesina@google.com
+toki@google.com
+tonymak@google.com
+mingweiliao@google.com
+anothermark@google.com
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index e370e85..59fca3b 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -151,6 +151,16 @@
}
flag {
+ name: "fix_avatar_cross_user_leak"
+ namespace: "multiuser"
+ description: "Fix cross-user picture uri leak for avatar picker apps."
+ bug: "341688848"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fix_get_user_property_cache"
namespace: "multiuser"
description: "Cache is not optimised for getUserProperty for values below 0, eg. UserHandler.USER_NULL or UserHandle.USER_ALL"
@@ -386,4 +396,7 @@
description: "Refactorings related to unicorn mode to work on HSUM mode (Read only flag)"
bug: "339201286"
is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 678bd6b..de1cac4 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -415,7 +415,7 @@
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
try {
- return new BiometricTestSession(mContext, sensorId,
+ return new BiometricTestSession(mContext, getSensorProperties(), sensorId,
(context, sensorId1, callback) -> mService
.createTestSession(sensorId1, callback, context.getOpPackageName()));
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
index 027d101..8bd3528 100644
--- a/core/java/android/hardware/biometrics/BiometricTestSession.java
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -27,12 +27,15 @@
import android.util.ArraySet;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
* {@link android.hardware.fingerprint.FingerprintManager}.
+ *
* @hide
*/
@TestApi
@@ -48,21 +51,29 @@
@NonNull ITestSessionCallback callback) throws RemoteException;
}
- private final Context mContext;
private final int mSensorId;
- private final ITestSession mTestSession;
+ private final List<ITestSession> mTestSessionsForAllSensors = new ArrayList<>();
+ private ITestSession mTestSession;
// Keep track of users that were tested, which need to be cleaned up when finishing.
- @NonNull private final ArraySet<Integer> mTestedUsers;
+ @NonNull
+ private final ArraySet<Integer> mTestedUsers;
// Track the users currently cleaning up, and provide a latch that gets notified when all
// users have finished cleaning up. This is an imperfect system, as there can technically be
// multiple cleanups per user. Theoretically we should track the cleanup's BaseClientMonitor's
// unique ID, but it's complicated to plumb it through. This should be fine for now.
- @Nullable private CountDownLatch mCloseLatch;
- @NonNull private final ArraySet<Integer> mUsersCleaningUp;
+ @Nullable
+ private CountDownLatch mCloseLatch;
+ @NonNull
+ private final ArraySet<Integer> mUsersCleaningUp;
- private final ITestSessionCallback mCallback = new ITestSessionCallback.Stub() {
+ private class TestSessionCallbackIml extends ITestSessionCallback.Stub {
+ private final int mSensorId;
+ private TestSessionCallbackIml(int sensorId) {
+ mSensorId = sensorId;
+ }
+
@Override
public void onCleanupStarted(int userId) {
Log.d(getTag(), "onCleanupStarted, sensor: " + mSensorId + ", userId: " + userId);
@@ -76,19 +87,30 @@
mUsersCleaningUp.remove(userId);
if (mUsersCleaningUp.isEmpty() && mCloseLatch != null) {
+ Log.d(getTag(), "counting down");
mCloseLatch.countDown();
}
}
- };
+ }
/**
* @hide
*/
- public BiometricTestSession(@NonNull Context context, int sensorId,
- @NonNull TestSessionProvider testSessionProvider) throws RemoteException {
- mContext = context;
+ public BiometricTestSession(@NonNull Context context, List<SensorProperties> sensors,
+ int sensorId, @NonNull TestSessionProvider testSessionProvider) throws RemoteException {
mSensorId = sensorId;
- mTestSession = testSessionProvider.createTestSession(context, sensorId, mCallback);
+ // When any of the sensors should create the test session, all the other sensors should
+ // set test hal enabled too.
+ for (SensorProperties sensor : sensors) {
+ final int id = sensor.getSensorId();
+ final ITestSession session = testSessionProvider.createTestSession(context, id,
+ new TestSessionCallbackIml(id));
+ mTestSessionsForAllSensors.add(session);
+ if (id == sensorId) {
+ mTestSession = session;
+ }
+ }
+
mTestedUsers = new ArraySet<>();
mUsersCleaningUp = new ArraySet<>();
setTestHalEnabled(true);
@@ -107,8 +129,11 @@
@RequiresPermission(TEST_BIOMETRIC)
private void setTestHalEnabled(boolean enabled) {
try {
- Log.w(getTag(), "setTestHalEnabled, sensor: " + mSensorId + " enabled: " + enabled);
- mTestSession.setTestHalEnabled(enabled);
+ for (ITestSession session : mTestSessionsForAllSensors) {
+ Log.w(getTag(), "setTestHalEnabled, sensor: " + session.getSensorId() + " enabled: "
+ + enabled);
+ session.setTestHalEnabled(enabled);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -175,10 +200,12 @@
/**
* Simulates an acquired message from the HAL.
*
- * @param userId User that this command applies to.
+ * @param userId User that this command applies to.
* @param acquireInfo See
- * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationAcquired(int)} and
- * {@link FingerprintManager.AuthenticationCallback#onAuthenticationAcquired(int)}
+ * {@link
+ * BiometricPrompt.AuthenticationCallback#onAuthenticationAcquired(int)} and
+ * {@link
+ * FingerprintManager.AuthenticationCallback#onAuthenticationAcquired(int)}
*/
@RequiresPermission(TEST_BIOMETRIC)
public void notifyAcquired(int userId, int acquireInfo) {
@@ -192,10 +219,12 @@
/**
* Simulates an error message from the HAL.
*
- * @param userId User that this command applies to.
+ * @param userId User that this command applies to.
* @param errorCode See
- * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationError(int, CharSequence)} and
- * {@link FingerprintManager.AuthenticationCallback#onAuthenticationError(int, CharSequence)}
+ * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} and
+ * {@link FingerprintManager.AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}
*/
@RequiresPermission(TEST_BIOMETRIC)
public void notifyError(int userId, int errorCode) {
@@ -220,8 +249,20 @@
Log.w(getTag(), "Cleanup already in progress for user: " + userId);
}
- mUsersCleaningUp.add(userId);
- mTestSession.cleanupInternalState(userId);
+ for (ITestSession session : mTestSessionsForAllSensors) {
+ mUsersCleaningUp.add(userId);
+ Log.d(getTag(), "cleanupInternalState for sensor: " + session.getSensorId());
+ mCloseLatch = new CountDownLatch(1);
+ session.cleanupInternalState(userId);
+
+ try {
+ Log.d(getTag(), "Awaiting latch...");
+ mCloseLatch.await(3, TimeUnit.SECONDS);
+ Log.d(getTag(), "Finished awaiting");
+ } catch (InterruptedException e) {
+ Log.e(getTag(), "Latch interrupted", e);
+ }
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -234,18 +275,9 @@
// Cleanup can be performed using the test HAL, since it always responds to enumerate with
// zero enrollments.
if (!mTestedUsers.isEmpty()) {
- mCloseLatch = new CountDownLatch(1);
for (int user : mTestedUsers) {
cleanupInternalState(user);
}
-
- try {
- Log.d(getTag(), "Awaiting latch...");
- mCloseLatch.await(3, TimeUnit.SECONDS);
- Log.d(getTag(), "Finished awaiting");
- } catch (InterruptedException e) {
- Log.e(getTag(), "Latch interrupted", e);
- }
}
if (!mUsersCleaningUp.isEmpty()) {
diff --git a/core/java/android/hardware/biometrics/ITestSession.aidl b/core/java/android/hardware/biometrics/ITestSession.aidl
index df9f504..bd99606 100644
--- a/core/java/android/hardware/biometrics/ITestSession.aidl
+++ b/core/java/android/hardware/biometrics/ITestSession.aidl
@@ -59,4 +59,8 @@
// HAL is disabled (e.g. to clean up after a test).
@EnforcePermission("TEST_BIOMETRIC")
void cleanupInternalState(int userId);
+
+ // Get the sensor id of the current test session.
+ @EnforcePermission("TEST_BIOMETRIC")
+ int getSensorId();
}
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 0c55ed5..9bd4860 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,8 +17,6 @@
package android.hardware.camera2.params;
-import static com.android.internal.util.Preconditions.*;
-
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -32,8 +30,6 @@
import android.hardware.camera2.CameraDevice.CameraDeviceSetup;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.params.InputConfiguration;
-import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.media.ImageReader;
import android.os.Parcel;
@@ -46,6 +42,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -95,8 +92,8 @@
public @interface SessionMode {};
// Camera capture session related parameters.
- private List<OutputConfiguration> mOutputConfigurations;
- private CameraCaptureSession.StateCallback mStateCallback;
+ private final @NonNull List<OutputConfiguration> mOutputConfigurations;
+ private CameraCaptureSession.StateCallback mStateCallback = null;
private int mSessionType;
private Executor mExecutor = null;
private InputConfiguration mInputConfig = null;
@@ -268,7 +265,8 @@
*/
@Override
public int hashCode() {
- return HashCodeHelpers.hashCode(mOutputConfigurations.hashCode(), mInputConfig.hashCode(),
+ return HashCodeHelpers.hashCode(mOutputConfigurations.hashCode(),
+ Objects.hashCode(mInputConfig),
mSessionType);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 903e916..7f1cac0 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -172,7 +172,7 @@
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
try {
- return new BiometricTestSession(mContext, sensorId,
+ return new BiometricTestSession(mContext, getSensorProperties(), sensorId,
(context, sensorId1, callback) -> mService
.createTestSession(sensorId1, callback, context.getOpPackageName()));
} catch (RemoteException e) {
diff --git a/core/java/android/os/ExternalVibrationScale.aidl b/core/java/android/os/ExternalVibrationScale.aidl
index cf6f8ed..644bece 100644
--- a/core/java/android/os/ExternalVibrationScale.aidl
+++ b/core/java/android/os/ExternalVibrationScale.aidl
@@ -33,12 +33,24 @@
SCALE_VERY_HIGH = 2
}
+ // TODO(b/345186129): remove this once we finish migrating to scale factor.
/**
* The scale level that will be applied to external vibrations.
*/
ScaleLevel scaleLevel = ScaleLevel.SCALE_NONE;
/**
+ * The scale factor that will be applied to external vibrations.
+ *
+ * Values in (0,1) will scale down the vibrations, values > 1 will scale up vibrations within
+ * hardware limits. A zero scale factor indicates the external vibration should be muted.
+ *
+ * TODO(b/345186129): update this once we finish migrating, negative should not be expected.
+ * Negative values should be ignored in favour of the legacy ScaleLevel.
+ */
+ float scaleFactor = -1f; // undefined
+
+ /**
* The adaptive haptics scale that will be applied to external vibrations.
*/
float adaptiveHapticsScale = 1f;
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index edb3a64..4a37e0a 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -520,8 +520,20 @@
* @param counterValue The counter value.
*/
public static void setCounter(@NonNull String counterName, long counterValue) {
- if (isTagEnabled(TRACE_TAG_APP)) {
- nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
+ setCounter(TRACE_TAG_APP, counterName, counterValue);
+ }
+
+ /**
+ * Writes trace message to indicate the value of a given counter under a given trace tag.
+ *
+ * @param traceTag The trace tag.
+ * @param counterName The counter name to appear in the trace.
+ * @param counterValue The counter value.
+ * @hide
+ */
+ public static void setCounter(long traceTag, @NonNull String counterName, long counterValue) {
+ if (isTagEnabled(traceTag)) {
+ nativeTraceCounter(traceTag, counterName, counterValue);
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 06c516a..28f2c25 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4824,6 +4824,7 @@
* <p>Note that this does not alter the user's pre-existing user restrictions.
*
* @param userId the id of the user to become admin
+ * @throws SecurityException if changing ADMIN status of the user is not allowed
* @hide
*/
@RequiresPermission(allOf = {
@@ -4844,6 +4845,7 @@
* <p>Note that this does not alter the user's pre-existing user restrictions.
*
* @param userId the id of the user to revoke admin rights from
+ * @throws SecurityException if changing ADMIN status of the user is not allowed
* @hide
*/
@RequiresPermission(allOf = {
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index f3ef9e1..e68b746 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -663,6 +663,15 @@
* @hide
*/
public static float scale(float intensity, float scaleFactor) {
+ if (Flags.hapticsScaleV2Enabled()) {
+ if (Float.compare(scaleFactor, 1) <= 0 || Float.compare(intensity, 0) == 0) {
+ // Scaling down or scaling zero intensity is straightforward.
+ return scaleFactor * intensity;
+ }
+ // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0.
+ return (scaleFactor * intensity) / (1 + (scaleFactor - 1) * intensity * intensity);
+ }
+
// Applying gamma correction to the scale factor, which is the same as encoding the input
// value, scaling it, then decoding the scaled value.
float scale = MathUtils.pow(scaleFactor, 1f / SCALE_GAMMA);
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index a4164e9..e6e5a27 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -49,8 +49,22 @@
*/
public class VibrationConfig {
+ /**
+ * Hardcoded default scale level gain to be applied between each scale level to define their
+ * scale factor value.
+ *
+ * <p>Default gain defined as 3 dBs.
+ */
+ private static final float DEFAULT_SCALE_LEVEL_GAIN = 1.4f;
+
+ /**
+ * Hardcoded default amplitude to be used when device config is invalid, i.e. not in [1,255].
+ */
+ private static final int DEFAULT_AMPLITUDE = 255;
+
// TODO(b/191150049): move these to vibrator static config file
private final float mHapticChannelMaxVibrationAmplitude;
+ private final int mDefaultVibrationAmplitude;
private final int mRampStepDurationMs;
private final int mRampDownDurationMs;
private final int mRequestVibrationParamsTimeoutMs;
@@ -75,8 +89,10 @@
/** @hide */
public VibrationConfig(@Nullable Resources resources) {
+ mDefaultVibrationAmplitude = resources.getInteger(
+ com.android.internal.R.integer.config_defaultVibrationAmplitude);
mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
- com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
+ com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude);
mRampDownDurationMs = loadInteger(resources,
com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0);
mRampStepDurationMs = loadInteger(resources,
@@ -87,9 +103,9 @@
com.android.internal.R.array.config_requestVibrationParamsForUsages);
mIgnoreVibrationsOnWirelessCharger = loadBoolean(resources,
- com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false);
+ com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger);
mKeyboardVibrationSettingsSupported = loadBoolean(resources,
- com.android.internal.R.bool.config_keyboardVibrationSettingsSupported, false);
+ com.android.internal.R.bool.config_keyboardVibrationSettingsSupported);
mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -115,16 +131,16 @@
return value;
}
- private static float loadFloat(@Nullable Resources res, int resId, float defaultValue) {
- return res != null ? res.getFloat(resId) : defaultValue;
+ private static float loadFloat(@Nullable Resources res, int resId) {
+ return res != null ? res.getFloat(resId) : 0f;
}
private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) {
return res != null ? res.getInteger(resId) : defaultValue;
}
- private static boolean loadBoolean(@Nullable Resources res, int resId, boolean defaultValue) {
- return res != null ? res.getBoolean(resId) : defaultValue;
+ private static boolean loadBoolean(@Nullable Resources res, int resId) {
+ return res != null && res.getBoolean(resId);
}
private static int[] loadIntArray(@Nullable Resources res, int resId) {
@@ -145,6 +161,26 @@
}
/**
+ * Return the device default vibration amplitude value to replace the
+ * {@link android.os.VibrationEffect#DEFAULT_AMPLITUDE} constant.
+ */
+ public int getDefaultVibrationAmplitude() {
+ if (mDefaultVibrationAmplitude < 1 || mDefaultVibrationAmplitude > 255) {
+ return DEFAULT_AMPLITUDE;
+ }
+ return mDefaultVibrationAmplitude;
+ }
+
+ /**
+ * Return the device default gain to be applied between scale levels to define the scale factor
+ * for each level.
+ */
+ public float getDefaultVibrationScaleLevelGain() {
+ // TODO(b/356407380): add device config for this
+ return DEFAULT_SCALE_LEVEL_GAIN;
+ }
+
+ /**
* The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator
* when a vibration is cancelled or finished at non-zero amplitude.
*/
@@ -233,6 +269,7 @@
public String toString() {
return "VibrationConfig{"
+ "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger
+ + ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude
+ ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+ ", mRampStepDurationMs=" + mRampStepDurationMs
+ ", mRampDownDurationMs=" + mRampDownDurationMs
@@ -258,6 +295,7 @@
pw.println("VibrationConfig:");
pw.increaseIndent();
pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger);
+ pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude);
pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude);
pw.println("rampStepDurationMs = " + mRampStepDurationMs);
pw.println("rampDownDurationMs = " + mRampDownDurationMs);
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 1a19bb2..53a1a67d 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -103,3 +103,13 @@
purpose: PURPOSE_FEATURE
}
}
+
+flag {
+ namespace: "haptics"
+ name: "haptics_scale_v2_enabled"
+ description: "Enables new haptics scaling function across all usages"
+ bug: "345186129"
+ metadata {
+ purpose: PURPOSE_FEATURE
+ }
+}
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
index a26c6f4..a95ce79 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
@@ -104,7 +104,7 @@
public static void serialize(@NonNull VibrationEffect effect, @NonNull Writer writer,
@Flags int flags) throws IOException {
// Serialize effect first to fail early.
- XmlSerializedVibration<VibrationEffect> serializedVibration =
+ XmlSerializedVibration<? extends VibrationEffect> serializedVibration =
toSerializedVibration(effect, flags);
TypedXmlSerializer xmlSerializer = Xml.newFastSerializer();
xmlSerializer.setFeature(XML_FEATURE_INDENT_OUTPUT, (flags & FLAG_PRETTY_PRINT) != 0);
@@ -114,9 +114,9 @@
xmlSerializer.endDocument();
}
- private static XmlSerializedVibration<VibrationEffect> toSerializedVibration(
+ private static XmlSerializedVibration<? extends VibrationEffect> toSerializedVibration(
VibrationEffect effect, @Flags int flags) throws SerializationFailedException {
- XmlSerializedVibration<VibrationEffect> serializedVibration;
+ XmlSerializedVibration<? extends VibrationEffect> serializedVibration;
int serializerFlags = 0;
if ((flags & FLAG_ALLOW_HIDDEN_APIS) != 0) {
serializerFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 224379b..fc6c2e8 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -25,7 +25,6 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
@@ -76,8 +75,9 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -1074,7 +1074,7 @@
rt.manualRule.type = AutomaticZenRule.TYPE_OTHER;
rt.manualRule.condition = new Condition(
rt.manualRule.conditionId != null ? rt.manualRule.conditionId
- : Uri.EMPTY, "", STATE_TRUE);
+ : Uri.EMPTY, "", Condition.STATE_TRUE);
}
}
return rt;
@@ -2540,10 +2540,34 @@
}
public static class ZenRule implements Parcelable {
+
+ /** No manual override. Rule owner can decide its state. */
+ public static final int OVERRIDE_NONE = 0;
+ /**
+ * User has manually activated a mode. This will temporarily overrule the rule owner's
+ * decision to deactivate it (see {@link #reconsiderConditionOverride}).
+ */
+ public static final int OVERRIDE_ACTIVATE = 1;
+ /**
+ * User has manually deactivated an active mode, or setting ZEN_MODE_OFF (for the few apps
+ * still allowed to do that) snoozed the mode. This will temporarily overrule the rule
+ * owner's decision to activate it (see {@link #reconsiderConditionOverride}).
+ */
+ public static final int OVERRIDE_DEACTIVATE = 2;
+
+ @IntDef(prefix = { "OVERRIDE" }, value = {
+ OVERRIDE_NONE,
+ OVERRIDE_ACTIVATE,
+ OVERRIDE_DEACTIVATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConditionOverride {}
+
@UnsupportedAppUsage
public boolean enabled;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public boolean snoozing; // user manually disabled this instance
+ @Deprecated
+ public boolean snoozing; // user manually disabled this instance. Obsolete with MODES_UI
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String name; // required for automatic
@UnsupportedAppUsage
@@ -2579,6 +2603,15 @@
// ZenPolicy, so we store them here, only for the manual rule.
@FlaggedApi(Flags.FLAG_MODES_UI)
int legacySuppressedEffects;
+ /**
+ * Signals a user's action to (temporarily or permanently) activate or deactivate this
+ * rule, overruling the condition set by the owner. This value is not stored to disk, as
+ * it shouldn't survive reboots or be involved in B&R. It might be reset by certain
+ * owner-provided state transitions as well.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_UI)
+ @ConditionOverride
+ int conditionOverride = OVERRIDE_NONE;
public ZenRule() { }
@@ -2620,6 +2653,7 @@
if (Flags.modesUi()) {
disabledOrigin = source.readInt();
legacySuppressedEffects = source.readInt();
+ conditionOverride = source.readInt();
}
}
}
@@ -2698,6 +2732,7 @@
if (Flags.modesUi()) {
dest.writeInt(disabledOrigin);
dest.writeInt(legacySuppressedEffects);
+ dest.writeInt(conditionOverride);
}
}
}
@@ -2708,9 +2743,16 @@
.append("id=").append(id)
.append(",state=").append(condition == null ? "STATE_FALSE"
: Condition.stateToString(condition.state))
- .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
- .append(",snoozing=").append(snoozing)
- .append(",name=").append(name)
+ .append(",enabled=").append(String.valueOf(enabled).toUpperCase());
+
+ if (Flags.modesUi()) {
+ sb.append(",conditionOverride=")
+ .append(conditionOverrideToString(conditionOverride));
+ } else {
+ sb.append(",snoozing=").append(snoozing);
+ }
+
+ sb.append(",name=").append(name)
.append(",zenMode=").append(Global.zenModeToString(zenMode))
.append(",conditionId=").append(conditionId)
.append(",pkg=").append(pkg)
@@ -2753,6 +2795,15 @@
return sb.append(']').toString();
}
+ private static String conditionOverrideToString(@ConditionOverride int value) {
+ return switch(value) {
+ case OVERRIDE_ACTIVATE -> "OVERRIDE_ACTIVATE";
+ case OVERRIDE_DEACTIVATE -> "OVERRIDE_DEACTIVATE";
+ case OVERRIDE_NONE -> "OVERRIDE_NONE";
+ default -> "UNKNOWN";
+ };
+ }
+
/** @hide */
// TODO: add configuration activity
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -2763,7 +2814,11 @@
proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
proto.write(ZenRuleProto.ENABLED, enabled);
proto.write(ZenRuleProto.ENABLER, enabler);
- proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
+ if (Flags.modesApi() && Flags.modesUi()) {
+ proto.write(ZenRuleProto.IS_SNOOZING, conditionOverride == OVERRIDE_DEACTIVATE);
+ } else {
+ proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
+ }
proto.write(ZenRuleProto.ZEN_MODE, zenMode);
if (conditionId != null) {
proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
@@ -2816,7 +2871,8 @@
if (Flags.modesUi()) {
finalEquals = finalEquals
&& other.disabledOrigin == disabledOrigin
- && other.legacySuppressedEffects == legacySuppressedEffects;
+ && other.legacySuppressedEffects == legacySuppressedEffects
+ && other.conditionOverride == conditionOverride;
}
}
@@ -2832,7 +2888,8 @@
zenDeviceEffects, modified, allowManualInvocation, iconResName,
triggerDescription, type, userModifiedFields,
zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
- deletionInstant, disabledOrigin, legacySuppressedEffects);
+ deletionInstant, disabledOrigin, legacySuppressedEffects,
+ conditionOverride);
} else {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
component, configurationActivity, pkg, id, enabler, zenPolicy,
@@ -2858,8 +2915,74 @@
}
}
+ // TODO: b/333527800 - Rename to isActive()
public boolean isAutomaticActive() {
- return enabled && !snoozing && getPkg() != null && isTrueOrUnknown();
+ if (Flags.modesApi() && Flags.modesUi()) {
+ if (!enabled || getPkg() == null) {
+ return false;
+ } else if (conditionOverride == OVERRIDE_ACTIVATE) {
+ return true;
+ } else if (conditionOverride == OVERRIDE_DEACTIVATE) {
+ return false;
+ } else {
+ return isTrueOrUnknown();
+ }
+ } else {
+ return enabled && !snoozing && getPkg() != null && isTrueOrUnknown();
+ }
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ @ConditionOverride
+ public int getConditionOverride() {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ return conditionOverride;
+ } else {
+ return snoozing ? OVERRIDE_DEACTIVATE : OVERRIDE_NONE;
+ }
+ }
+
+ public void setConditionOverride(@ConditionOverride int value) {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ conditionOverride = value;
+ } else {
+ if (value == OVERRIDE_ACTIVATE) {
+ Slog.wtf(TAG, "Shouldn't set OVERRIDE_ACTIVATE if MODES_UI is off");
+ } else if (value == OVERRIDE_DEACTIVATE) {
+ snoozing = true;
+ } else if (value == OVERRIDE_NONE) {
+ snoozing = false;
+ }
+ }
+ }
+
+ public void resetConditionOverride() {
+ setConditionOverride(OVERRIDE_NONE);
+ }
+
+ /**
+ * Possibly remove the override, depending on the rule owner's intended state.
+ *
+ * <p>This allows rule owners to "take over" manually-provided state with their smartness,
+ * but only once both agree.
+ *
+ * <p>For example, a manually activated rule wins over rule owner's opinion that it should
+ * be off, until the owner says it should be on, at which point it will turn off (without
+ * manual intervention) when the rule owner says it should be off. And symmetrically for
+ * manual deactivation (which used to be called "snoozing").
+ */
+ public void reconsiderConditionOverride() {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ if (conditionOverride == OVERRIDE_ACTIVATE && isTrueOrUnknown()) {
+ resetConditionOverride();
+ } else if (conditionOverride == OVERRIDE_DEACTIVATE && !isTrueOrUnknown()) {
+ resetConditionOverride();
+ }
+ } else {
+ if (snoozing && !isTrueOrUnknown()) {
+ snoozing = false;
+ }
+ }
}
public String getPkg() {
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index a37e227..05c2a9c 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -454,6 +454,8 @@
*/
public static class RuleDiff extends BaseDiff {
public static final String FIELD_ENABLED = "enabled";
+ public static final String FIELD_CONDITION_OVERRIDE = "conditionOverride";
+ @Deprecated
public static final String FIELD_SNOOZING = "snoozing";
public static final String FIELD_NAME = "name";
public static final String FIELD_ZEN_MODE = "zenMode";
@@ -507,8 +509,15 @@
if (from.enabled != to.enabled) {
addField(FIELD_ENABLED, new FieldDiff<>(from.enabled, to.enabled));
}
- if (from.snoozing != to.snoozing) {
- addField(FIELD_SNOOZING, new FieldDiff<>(from.snoozing, to.snoozing));
+ if (Flags.modesApi() && Flags.modesUi()) {
+ if (from.conditionOverride != to.conditionOverride) {
+ addField(FIELD_CONDITION_OVERRIDE,
+ new FieldDiff<>(from.conditionOverride, to.conditionOverride));
+ }
+ } else {
+ if (from.snoozing != to.snoozing) {
+ addField(FIELD_SNOOZING, new FieldDiff<>(from.snoozing, to.snoozing));
+ }
}
if (!Objects.equals(from.name, to.name)) {
addField(FIELD_NAME, new FieldDiff<>(from.name, to.name));
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a7641c0..9e4b27d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3574,7 +3574,7 @@
checkPreconditions(sc);
if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
- "reparent", this, sc,
+ "setColor", this, sc,
"r=" + color[0] + " g=" + color[1] + " b=" + color[2]);
}
nativeSetColor(mNativeObject, sc.mNativeObject, color);
diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java
index f72a5ca..f210741 100644
--- a/core/java/com/android/internal/graphics/ColorUtils.java
+++ b/core/java/com/android/internal/graphics/ColorUtils.java
@@ -21,7 +21,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.graphics.Color;
-
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.internal.graphics.cam.Cam;
/**
@@ -29,6 +29,7 @@
*
* A set of color-related utility methods, building upon those available in {@code Color}.
*/
+@RavenwoodKeepWholeClass
public final class ColorUtils {
private static final double XYZ_WHITE_REFERENCE_X = 95.047;
@@ -696,4 +697,4 @@
double calculateContrast(int foreground, int background, int alpha);
}
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/graphics/cam/Cam.java b/core/java/com/android/internal/graphics/cam/Cam.java
index 1df85c3..49fa37b 100644
--- a/core/java/com/android/internal/graphics/cam/Cam.java
+++ b/core/java/com/android/internal/graphics/cam/Cam.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.internal.graphics.ColorUtils;
@@ -25,6 +26,7 @@
* A color appearance model, based on CAM16, extended to use L* as the lightness dimension, and
* coupled to a gamut mapping algorithm. Creates a color system, enables a digital design system.
*/
+@RavenwoodKeepWholeClass
public class Cam {
// The maximum difference between the requested L* and the L* returned.
private static final float DL_MAX = 0.2f;
diff --git a/core/java/com/android/internal/graphics/cam/CamUtils.java b/core/java/com/android/internal/graphics/cam/CamUtils.java
index f541729..76fabc6 100644
--- a/core/java/com/android/internal/graphics/cam/CamUtils.java
+++ b/core/java/com/android/internal/graphics/cam/CamUtils.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.graphics.Color;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.internal.graphics.ColorUtils;
@@ -45,6 +46,7 @@
* consistent, and reasonably good. It worked." - Fairchild, Color Models and Systems: Handbook of
* Color Psychology, 2015
*/
+@RavenwoodKeepWholeClass
public final class CamUtils {
private CamUtils() {
}
diff --git a/core/java/com/android/internal/graphics/cam/Frame.java b/core/java/com/android/internal/graphics/cam/Frame.java
index 0ac7cbc..c419fab 100644
--- a/core/java/com/android/internal/graphics/cam/Frame.java
+++ b/core/java/com/android/internal/graphics/cam/Frame.java
@@ -17,6 +17,7 @@
package com.android.internal.graphics.cam;
import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.util.MathUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -33,6 +34,7 @@
* number of calculations during the color => CAM conversion process that depend only on the viewing
* conditions. Caching those calculations in a Frame instance saves a significant amount of time.
*/
+@RavenwoodKeepWholeClass
public final class Frame {
// Standard viewing conditions assumed in RGB specification - Stokes, Anderson, Chandrasekar,
// Motta - A Standard Default Color Space for the Internet: sRGB, 1996.
diff --git a/core/java/com/android/internal/graphics/cam/HctSolver.java b/core/java/com/android/internal/graphics/cam/HctSolver.java
index d7a8691..6e558e7 100644
--- a/core/java/com/android/internal/graphics/cam/HctSolver.java
+++ b/core/java/com/android/internal/graphics/cam/HctSolver.java
@@ -16,6 +16,8 @@
package com.android.internal.graphics.cam;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+
/**
* An efficient algorithm for determining the closest sRGB color to a set of HCT coordinates,
* based on geometrical insights for finding intersections in linear RGB, CAM16, and L*a*b*.
@@ -24,6 +26,7 @@
* Copied from //java/com/google/ux/material/libmonet/hct on May 22 2022.
* ColorUtils/MathUtils functions that were required were added to CamUtils.
*/
+@RavenwoodKeepWholeClass
public class HctSolver {
private HctSolver() {}
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index fbec1f1..e0c90d8 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -332,6 +332,8 @@
}
private void onTracingFlush() {
+ Log.d(LOG_TAG, "Executing onTracingFlush");
+
final ExecutorService loggingService;
try {
mBackgroundServiceLock.lock();
@@ -352,15 +354,19 @@
Log.e(LOG_TAG, "Failed to wait for tracing to finish", e);
}
- dumpTransitionTraceConfig();
+ dumpViewerConfig();
+
+ Log.d(LOG_TAG, "Finished onTracingFlush");
}
- private void dumpTransitionTraceConfig() {
+ private void dumpViewerConfig() {
if (mViewerConfigInputStreamProvider == null) {
// No viewer config available
return;
}
+ Log.d(LOG_TAG, "Dumping viewer config to trace");
+
ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
if (pis == null) {
@@ -390,6 +396,8 @@
Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
}
});
+
+ Log.d(LOG_TAG, "Dumped viewer config to trace");
}
private static void writeViewerConfigGroup(
@@ -770,6 +778,8 @@
private synchronized void onTracingInstanceStart(
int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
+ Log.d(LOG_TAG, "Executing onTracingInstanceStart");
+
final LogLevel defaultLogFrom = config.getDefaultGroupConfig().logFrom;
for (int i = defaultLogFrom.ordinal(); i < LogLevel.values().length; i++) {
mDefaultLogLevelCounts[i]++;
@@ -800,10 +810,13 @@
mCacheUpdater.run();
this.mTracingInstances.incrementAndGet();
+
+ Log.d(LOG_TAG, "Finished onTracingInstanceStart");
}
private synchronized void onTracingInstanceStop(
int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
+ Log.d(LOG_TAG, "Executing onTracingInstanceStop");
this.mTracingInstances.decrementAndGet();
final LogLevel defaultLogFrom = config.getDefaultGroupConfig().logFrom;
@@ -835,6 +848,7 @@
}
mCacheUpdater.run();
+ Log.d(LOG_TAG, "Finished onTracingInstanceStop");
}
private static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java b/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
index 15ecedd..cd7dcfd 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
@@ -29,7 +29,7 @@
import android.util.IntArray;
import android.util.LongArray;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedVibrationEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedComposedEffect.java
similarity index 86%
rename from core/java/com/android/internal/vibrator/persistence/SerializedVibrationEffect.java
rename to core/java/com/android/internal/vibrator/persistence/SerializedComposedEffect.java
index 23df304..6c562c9 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedVibrationEffect.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedComposedEffect.java
@@ -29,24 +29,24 @@
import java.util.Arrays;
/**
- * Serialized representation of a {@link VibrationEffect}.
+ * Serialized representation of a {@link VibrationEffect.Composed}.
*
* <p>The vibration is represented by a list of serialized segments that can be added to a
* {@link VibrationEffect.Composition} during the {@link #deserialize()} procedure.
*
* @hide
*/
-final class SerializedVibrationEffect implements XmlSerializedVibration<VibrationEffect> {
+final class SerializedComposedEffect implements XmlSerializedVibration<VibrationEffect.Composed> {
@NonNull
private final SerializedSegment[] mSegments;
- SerializedVibrationEffect(@NonNull SerializedSegment segment) {
+ SerializedComposedEffect(@NonNull SerializedSegment segment) {
requireNonNull(segment);
mSegments = new SerializedSegment[]{ segment };
}
- SerializedVibrationEffect(@NonNull SerializedSegment[] segments) {
+ SerializedComposedEffect(@NonNull SerializedSegment[] segments) {
requireNonNull(segments);
checkArgument(segments.length > 0, "Unsupported empty vibration");
mSegments = segments;
@@ -54,12 +54,12 @@
@NonNull
@Override
- public VibrationEffect deserialize() {
+ public VibrationEffect.Composed deserialize() {
VibrationEffect.Composition composition = VibrationEffect.startComposition();
for (SerializedSegment segment : mSegments) {
segment.deserializeIntoComposition(composition);
}
- return composition.compose();
+ return (VibrationEffect.Composed) composition.compose();
}
@Override
@@ -79,7 +79,7 @@
@Override
public String toString() {
- return "SerializedVibrationEffect{"
+ return "SerializedComposedEffect{"
+ "segments=" + Arrays.toString(mSegments)
+ '}';
}
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java b/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java
index db5c7ff..862f7cb 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java
@@ -27,7 +27,7 @@
import android.os.VibrationEffect;
import android.os.vibrator.PrimitiveSegment;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedPredefinedEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedPredefinedEffect.java
index 8924311..a6f48a4 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedPredefinedEffect.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedPredefinedEffect.java
@@ -25,7 +25,7 @@
import android.os.VibrationEffect;
import android.os.vibrator.PrebakedSegment;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.internal.vibrator.persistence.XmlConstants.PredefinedEffectName;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedVendorEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedVendorEffect.java
new file mode 100644
index 0000000..aa1b0a23
--- /dev/null
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedVendorEffect.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2024 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.internal.vibrator.persistence;
+
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VENDOR_EFFECT;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.os.PersistableBundle;
+import android.os.VibrationEffect;
+import android.text.TextUtils;
+import android.util.Base64;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Serialized representation of a {@link VibrationEffect.VendorEffect}.
+ *
+ * <p>The vibration is represented by an opaque {@link PersistableBundle} that can be used by
+ * {@link VibrationEffect#createVendorEffect(PersistableBundle)} during the {@link #deserialize()}
+ * procedure.
+ *
+ * @hide
+ */
+final class SerializedVendorEffect implements XmlSerializedVibration<VibrationEffect.VendorEffect> {
+
+ @NonNull
+ private final PersistableBundle mVendorData;
+
+ SerializedVendorEffect(@NonNull PersistableBundle vendorData) {
+ requireNonNull(vendorData);
+ mVendorData = vendorData;
+ }
+
+ @SuppressLint("MissingPermission")
+ @NonNull
+ @Override
+ public VibrationEffect.VendorEffect deserialize() {
+ return (VibrationEffect.VendorEffect) VibrationEffect.createVendorEffect(mVendorData);
+ }
+
+ @Override
+ public void write(@NonNull TypedXmlSerializer serializer)
+ throws IOException {
+ serializer.startTag(XmlConstants.NAMESPACE, XmlConstants.TAG_VIBRATION_EFFECT);
+ writeContent(serializer);
+ serializer.endTag(XmlConstants.NAMESPACE, XmlConstants.TAG_VIBRATION_EFFECT);
+ }
+
+ @Override
+ public void writeContent(@NonNull TypedXmlSerializer serializer) throws IOException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ mVendorData.writeToStream(outputStream);
+
+ serializer.startTag(XmlConstants.NAMESPACE, XmlConstants.TAG_VENDOR_EFFECT);
+ serializer.text(Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP));
+ serializer.endTag(XmlConstants.NAMESPACE, XmlConstants.TAG_VENDOR_EFFECT);
+ }
+
+ @Override
+ public String toString() {
+ return "SerializedVendorEffect{"
+ + "vendorData=" + mVendorData
+ + '}';
+ }
+
+ /** Parser implementation for {@link SerializedVendorEffect}. */
+ static final class Parser {
+
+ @NonNull
+ static SerializedVendorEffect parseNext(@NonNull TypedXmlPullParser parser,
+ @XmlConstants.Flags int flags) throws XmlParserException, IOException {
+ XmlValidator.checkStartTag(parser, TAG_VENDOR_EFFECT);
+ XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
+
+ PersistableBundle vendorData;
+ XmlReader.readNextText(parser, TAG_VENDOR_EFFECT);
+
+ try {
+ String text = parser.getText().trim();
+ XmlValidator.checkParserCondition(!text.isEmpty(),
+ "Expected tag %s to have base64 representation of vendor data, got empty",
+ TAG_VENDOR_EFFECT);
+
+ vendorData = PersistableBundle.readFromStream(
+ new ByteArrayInputStream(Base64.decode(text, Base64.DEFAULT)));
+ XmlValidator.checkParserCondition(!vendorData.isEmpty(),
+ "Expected tag %s to have non-empty vendor data, got empty bundle",
+ TAG_VENDOR_EFFECT);
+ } catch (IllegalArgumentException | NullPointerException e) {
+ throw new XmlParserException(
+ TextUtils.formatSimple(
+ "Expected base64 representation of vendor data in tag %s, got %s",
+ TAG_VENDOR_EFFECT, parser.getText()),
+ e);
+ } catch (IOException e) {
+ throw new XmlParserException("Error reading vendor data from decoded bytes", e);
+ }
+
+ // Consume tag
+ XmlReader.readEndTag(parser);
+
+ return new SerializedVendorEffect(vendorData);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
index 2b8b61d..a9fbcaf 100644
--- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
+++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
@@ -18,13 +18,15 @@
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREDEFINED_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITIVE_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VENDOR_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VIBRATION_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_EFFECT;
import android.annotation.NonNull;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.modules.utils.TypedXmlPullParser;
import java.io.IOException;
@@ -80,6 +82,16 @@
* }
* </pre>
*
+ * * Vendor vibration effects
+ *
+ * <pre>
+ * {@code
+ * <vibration-effect>
+ * <vendor-effect>base64-representation-of-persistable-bundle</vendor-effect>
+ * </vibration-effect>
+ * }
+ * </pre>
+ *
* @hide
*/
public class VibrationEffectXmlParser {
@@ -87,11 +99,9 @@
/**
* Parses the current XML tag with all nested tags into a single {@link XmlSerializedVibration}
* wrapping a {@link VibrationEffect}.
- *
- * @see XmlParser#parseTag(TypedXmlPullParser)
*/
@NonNull
- public static XmlSerializedVibration<VibrationEffect> parseTag(
+ public static XmlSerializedVibration<? extends VibrationEffect> parseTag(
@NonNull TypedXmlPullParser parser, @XmlConstants.Flags int flags)
throws XmlParserException, IOException {
XmlValidator.checkStartTag(parser, TAG_VIBRATION_EFFECT);
@@ -107,8 +117,9 @@
* <p>This can be reused for reading a vibration from an XML root tag or from within a combined
* vibration, but it should always be called from places that validates the top level tag.
*/
- static SerializedVibrationEffect parseVibrationContent(TypedXmlPullParser parser,
- @XmlConstants.Flags int flags) throws XmlParserException, IOException {
+ private static XmlSerializedVibration<? extends VibrationEffect> parseVibrationContent(
+ TypedXmlPullParser parser, @XmlConstants.Flags int flags)
+ throws XmlParserException, IOException {
String vibrationTagName = parser.getName();
int vibrationTagDepth = parser.getDepth();
@@ -116,11 +127,16 @@
XmlReader.readNextTagWithin(parser, vibrationTagDepth),
"Unsupported empty vibration tag");
- SerializedVibrationEffect serializedVibration;
+ XmlSerializedVibration<? extends VibrationEffect> serializedVibration;
switch (parser.getName()) {
+ case TAG_VENDOR_EFFECT:
+ if (Flags.vendorVibrationEffects()) {
+ serializedVibration = SerializedVendorEffect.Parser.parseNext(parser, flags);
+ break;
+ } // else fall through
case TAG_PREDEFINED_EFFECT:
- serializedVibration = new SerializedVibrationEffect(
+ serializedVibration = new SerializedComposedEffect(
SerializedPredefinedEffect.Parser.parseNext(parser, flags));
break;
case TAG_PRIMITIVE_EFFECT:
@@ -128,11 +144,11 @@
do { // First primitive tag already open
primitives.add(SerializedCompositionPrimitive.Parser.parseNext(parser));
} while (XmlReader.readNextTagWithin(parser, vibrationTagDepth));
- serializedVibration = new SerializedVibrationEffect(
+ serializedVibration = new SerializedComposedEffect(
primitives.toArray(new SerializedSegment[primitives.size()]));
break;
case TAG_WAVEFORM_EFFECT:
- serializedVibration = new SerializedVibrationEffect(
+ serializedVibration = new SerializedComposedEffect(
SerializedAmplitudeStepWaveform.Parser.parseNext(parser));
break;
default:
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
index f561c14..d74a23d 100644
--- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
+++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
@@ -17,13 +17,15 @@
package com.android.internal.vibrator.persistence;
import android.annotation.NonNull;
+import android.os.PersistableBundle;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.internal.vibrator.persistence.XmlConstants.PredefinedEffectName;
import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName;
@@ -41,6 +43,7 @@
* <li>{@link VibrationEffect#createWaveform(long[], int[], int)}
* <li>A composition created exclusively via
* {@link VibrationEffect.Composition#addPrimitive(int, float, int)}
+ * <li>{@link VibrationEffect#createVendorEffect(PersistableBundle)}
* </ul>
*
* @hide
@@ -49,13 +52,16 @@
/**
* Creates a serialized representation of the input {@code vibration}.
- *
- * @see XmlSerializer#serialize
*/
@NonNull
- public static XmlSerializedVibration<VibrationEffect> serialize(
+ public static XmlSerializedVibration<? extends VibrationEffect> serialize(
@NonNull VibrationEffect vibration, @XmlConstants.Flags int flags)
throws XmlSerializerException {
+ if (Flags.vendorVibrationEffects()
+ && (vibration instanceof VibrationEffect.VendorEffect vendorEffect)) {
+ return serializeVendorEffect(vendorEffect);
+ }
+
XmlValidator.checkSerializerCondition(vibration instanceof VibrationEffect.Composed,
"Unsupported VibrationEffect type %s", vibration);
@@ -73,7 +79,7 @@
return serializeWaveformEffect(composed);
}
- private static SerializedVibrationEffect serializePredefinedEffect(
+ private static SerializedComposedEffect serializePredefinedEffect(
VibrationEffect.Composed effect, @XmlConstants.Flags int flags)
throws XmlSerializerException {
List<VibrationEffectSegment> segments = effect.getSegments();
@@ -81,10 +87,15 @@
"Unsupported repeating predefined effect %s", effect);
XmlValidator.checkSerializerCondition(segments.size() == 1,
"Unsupported multiple segments in predefined effect %s", effect);
- return new SerializedVibrationEffect(serializePrebakedSegment(segments.get(0), flags));
+ return new SerializedComposedEffect(serializePrebakedSegment(segments.get(0), flags));
}
- private static SerializedVibrationEffect serializePrimitiveEffect(
+ private static SerializedVendorEffect serializeVendorEffect(
+ VibrationEffect.VendorEffect effect) {
+ return new SerializedVendorEffect(effect.getVendorData());
+ }
+
+ private static SerializedComposedEffect serializePrimitiveEffect(
VibrationEffect.Composed effect) throws XmlSerializerException {
List<VibrationEffectSegment> segments = effect.getSegments();
XmlValidator.checkSerializerCondition(effect.getRepeatIndex() == -1,
@@ -95,10 +106,10 @@
primitives[i] = serializePrimitiveSegment(segments.get(i));
}
- return new SerializedVibrationEffect(primitives);
+ return new SerializedComposedEffect(primitives);
}
- private static SerializedVibrationEffect serializeWaveformEffect(
+ private static SerializedComposedEffect serializeWaveformEffect(
VibrationEffect.Composed effect) throws XmlSerializerException {
SerializedAmplitudeStepWaveform.Builder serializedWaveformBuilder =
new SerializedAmplitudeStepWaveform.Builder();
@@ -120,7 +131,7 @@
segment.getDuration(), toAmplitudeInt(segment.getAmplitude()));
}
- return new SerializedVibrationEffect(serializedWaveformBuilder.build());
+ return new SerializedComposedEffect(serializedWaveformBuilder.build());
}
private static SerializedPredefinedEffect serializePrebakedSegment(
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
index 8b92153..2a55d99 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
@@ -40,6 +40,7 @@
public static final String TAG_PREDEFINED_EFFECT = "predefined-effect";
public static final String TAG_PRIMITIVE_EFFECT = "primitive-effect";
+ public static final String TAG_VENDOR_EFFECT = "vendor-effect";
public static final String TAG_WAVEFORM_EFFECT = "waveform-effect";
public static final String TAG_WAVEFORM_ENTRY = "waveform-entry";
public static final String TAG_REPEATING = "repeating";
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlParser.java b/core/java/com/android/internal/vibrator/persistence/XmlParser.java
deleted file mode 100644
index 6712f1c..0000000
--- a/core/java/com/android/internal/vibrator/persistence/XmlParser.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.internal.vibrator.persistence;
-
-import android.annotation.NonNull;
-
-import com.android.modules.utils.TypedXmlPullParser;
-
-import java.io.IOException;
-
-/**
- * Parse XML tags into valid {@link XmlSerializedVibration} instances.
- *
- * @param <T> The vibration type that will be parsed.
- * @see XmlSerializedVibration
- * @hide
- */
-@FunctionalInterface
-public interface XmlParser<T> {
-
- /**
- * Parses the current XML tag with all nested tags into a single {@link XmlSerializedVibration}.
- *
- * <p>This method will consume nested XML tags until it finds the
- * {@link TypedXmlPullParser#END_TAG} for the current tag.
- *
- * <p>The vibration reconstructed by the returned {@link XmlSerializedVibration#deserialize()}
- * is guaranteed to be valid. This method will throw an exception otherwise.
- *
- * @param pullParser The {@link TypedXmlPullParser} with the input XML.
- * @return The parsed vibration wrapped in a {@link XmlSerializedVibration} representation.
- * @throws IOException On any I/O error while reading the input XML
- * @throws XmlParserException If the XML content does not represent a valid vibration.
- */
- XmlSerializedVibration<T> parseTag(@NonNull TypedXmlPullParser pullParser)
- throws XmlParserException, IOException;
-}
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlParserException.java b/core/java/com/android/internal/vibrator/persistence/XmlParserException.java
index 7507864..e2b30e7 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlParserException.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlParserException.java
@@ -23,7 +23,6 @@
/**
* Represents an error while parsing a vibration XML input.
*
- * @see XmlParser
* @hide
*/
public final class XmlParserException extends Exception {
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlReader.java b/core/java/com/android/internal/vibrator/persistence/XmlReader.java
index a5ace84..0ac6fef 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlReader.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlReader.java
@@ -130,6 +130,25 @@
}
/**
+ * Read the next element, ignoring comments and ignorable whitespace, and returns only if it's a
+ * {@link XmlPullParser#TEXT}. Any other tag will fail this check.
+ *
+ * <p>The parser will be pointing to the first next element after skipping comments,
+ * instructions and ignorable whitespace.
+ */
+ public static void readNextText(TypedXmlPullParser parser, String tagName)
+ throws XmlParserException, IOException {
+ try {
+ int type = parser.next(); // skips comments, instruction tokens and ignorable whitespace
+ XmlValidator.checkParserCondition(type == XmlPullParser.TEXT,
+ "Unexpected event %s of type %d, expected text event inside tag %s",
+ parser.getName(), type, tagName);
+ } catch (XmlPullParserException e) {
+ throw XmlParserException.createFromPullParserException("text event", e);
+ }
+ }
+
+ /**
* Check parser has a {@link XmlPullParser#END_TAG} as the next tag, with no nested tags.
*
* <p>The parser will be pointing to the end tag after this method.
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlSerializedVibration.java b/core/java/com/android/internal/vibrator/persistence/XmlSerializedVibration.java
index 3233fa2..c20b7d2 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlSerializedVibration.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlSerializedVibration.java
@@ -26,8 +26,7 @@
* Serialized representation of a generic vibration.
*
* <p>This can be used to represent a {@link android.os.CombinedVibration} or a
- * {@link android.os.VibrationEffect}. Instances can be created from vibration objects via
- * {@link XmlSerializer}, or from XML content via {@link XmlParser}.
+ * {@link android.os.VibrationEffect}.
*
* <p>The separation of serialization and writing procedures enables configurable rules to define
* which vibrations can be successfully serialized before any data is written to the output stream.
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlSerializer.java b/core/java/com/android/internal/vibrator/persistence/XmlSerializer.java
deleted file mode 100644
index 102e6c1..0000000
--- a/core/java/com/android/internal/vibrator/persistence/XmlSerializer.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.internal.vibrator.persistence;
-
-import android.annotation.NonNull;
-
-/**
- * Creates a {@link XmlSerializedVibration} instance representing a vibration.
- *
- * @param <T> The vibration type that will be serialized.
- * @see XmlSerializedVibration
- * @hide
- */
-@FunctionalInterface
-public interface XmlSerializer<T> {
-
- /**
- * Creates a serialized representation of the input {@code vibration}.
- *
- * @param vibration The vibration to be serialized
- * @return The serialized representation of the input vibration
- * @throws XmlSerializerException If the input vibration cannot be serialized
- */
- @NonNull
- XmlSerializedVibration<T> serialize(@NonNull T vibration) throws XmlSerializerException;
-}
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlSerializerException.java b/core/java/com/android/internal/vibrator/persistence/XmlSerializerException.java
index c57ff5d..2e7ad09 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlSerializerException.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlSerializerException.java
@@ -19,7 +19,6 @@
/**
* Represents an error while serializing a vibration input.
*
- * @see XmlSerializer
* @hide
*/
public final class XmlSerializerException extends Exception {
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlValidator.java b/core/java/com/android/internal/vibrator/persistence/XmlValidator.java
index 84d4f3f..1b5a356 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlValidator.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlValidator.java
@@ -18,7 +18,7 @@
import static java.util.Objects.requireNonNull;
-import android.annotation.NonNull;
+import android.os.VibrationEffect;
import android.text.TextUtils;
import com.android.internal.util.ArrayUtils;
@@ -82,11 +82,11 @@
* Check given {@link XmlSerializedVibration} represents the expected {@code vibration} object
* when it's deserialized.
*/
- @NonNull
- public static <T> void checkSerializedVibration(
- XmlSerializedVibration<T> serializedVibration, T expectedVibration)
+ public static void checkSerializedVibration(
+ XmlSerializedVibration<? extends VibrationEffect> serializedVibration,
+ VibrationEffect expectedVibration)
throws XmlSerializerException {
- T deserializedVibration = requireNonNull(serializedVibration.deserialize());
+ VibrationEffect deserializedVibration = requireNonNull(serializedVibration.deserialize());
checkSerializerCondition(Objects.equals(expectedVibration, deserializedVibration),
"Unexpected serialized vibration %s: found deserialization %s, expected %s",
serializedVibration, deserializedVibration, expectedVibration);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 03b5143a..db6fb23 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1023,10 +1023,22 @@
parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,
"--compiler-filter=", "-Ximage-compiler-option");
- // If there is a dirty-image-objects file, push it.
- if (hasFile("/system/etc/dirty-image-objects")) {
- addOption("-Ximage-compiler-option");
- addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
+ // If there are dirty-image-objects files, push them.
+ const char* dirty_image_objects_options[] = {
+ // Currently, there are two dirty-image-objects files: one for
+ // ART module, one for framework.
+ "--dirty-image-objects=/system/etc/dirty-image-objects.txt",
+ "--dirty-image-objects=/apex/com.android.art/etc/dirty-image-objects.txt",
+ // Allow old filename (without .txt) for backward compatibility.
+ "--dirty-image-objects=/system/etc/dirty-image-objects",
+ };
+ for (const char* option : dirty_image_objects_options) {
+ // Get the file path by finding the first '/' and check if
+ // this file exists.
+ if (hasFile(strchr(option, '/'))) {
+ addOption("-Ximage-compiler-option");
+ addOption(option);
+ }
}
parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 12804d4..e7f0560 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -163,7 +163,7 @@
optional bool vibrator_under_external_control = 5;
optional bool low_power_mode = 6;
optional bool vibrate_on = 24;
- optional bool keyboard_vibration_on = 25;
+ reserved 25; // prev keyboard_vibration_on
optional int32 default_vibration_amplitude = 26;
optional int32 alarm_intensity = 18;
optional int32 alarm_default_intensity = 19;
diff --git a/core/res/res/layout/list_menu_item_icon.xml b/core/res/res/layout/list_menu_item_icon.xml
index a30be6a..5854e81 100644
--- a/core/res/res/layout/list_menu_item_icon.xml
+++ b/core/res/res/layout/list_menu_item_icon.xml
@@ -18,6 +18,8 @@
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:maxWidth="@dimen/list_menu_item_icon_max_width"
+ android:adjustViewBounds="true"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dip"
android:layout_marginEnd="-8dip"
diff --git a/core/res/res/layout/time_picker_text_input_material.xml b/core/res/res/layout/time_picker_text_input_material.xml
index 4988842..86070b1 100644
--- a/core/res/res/layout/time_picker_text_input_material.xml
+++ b/core/res/res/layout/time_picker_text_input_material.xml
@@ -34,19 +34,29 @@
android:layoutDirection="ltr">
<EditText
android:id="@+id/input_hour"
- android:layout_width="50dp"
+ android:layout_width="50sp"
android:layout_height="wrap_content"
+ android:layout_alignEnd="@id/hour_label_holder"
android:inputType="number"
android:textAppearance="@style/TextAppearance.Material.TimePicker.InputField"
android:imeOptions="actionNext"/>
- <TextView
- android:id="@+id/label_hour"
+ <!-- Ensure the label_hour takes up at least 50sp of space -->
+ <FrameLayout
+ android:id="@+id/hour_label_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_below="@id/input_hour"
- android:layout_alignStart="@id/input_hour"
- android:labelFor="@+id/input_hour"
- android:text="@string/time_picker_hour_label"/>
+ android:layout_below="@id/input_hour">
+ <TextView
+ android:id="@+id/label_hour"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:labelFor="@+id/input_hour"
+ android:text="@string/time_picker_hour_label"/>
+ <Space
+ android:layout_width="50sp"
+ android:layout_height="0dp"/>
+ </FrameLayout>
<TextView
android:id="@+id/input_separator"
@@ -58,21 +68,30 @@
<EditText
android:id="@+id/input_minute"
- android:layout_width="50dp"
+ android:layout_width="50sp"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/input_hour"
android:layout_toEndOf="@id/input_separator"
android:inputType="number"
android:textAppearance="@style/TextAppearance.Material.TimePicker.InputField" />
- <TextView
- android:id="@+id/label_minute"
+ <!-- Ensure the label_minute takes up at least 50sp of space -->
+ <FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/input_minute"
android:layout_alignStart="@id/input_minute"
- android:labelFor="@+id/input_minute"
- android:text="@string/time_picker_minute_label"/>
-
+ >
+ <TextView
+ android:id="@+id/label_minute"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:labelFor="@+id/input_minute"
+ android:text="@string/time_picker_minute_label"/>
+ <Space
+ android:layout_width="50sp"
+ android:layout_height="0dp"/>
+ </FrameLayout>
<TextView
android:visibility="invisible"
android:id="@+id/label_error"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 77b5587..f397ef2 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -1065,4 +1065,7 @@
<!-- The non-linear progress interval when the screen is wider than the
navigation_edge_action_progress_threshold. -->
<item name="back_progress_non_linear_factor" format="float" type="dimen">0.2</item>
+
+ <!-- The maximum width for a context menu icon -->
+ <dimen name="list_menu_item_icon_max_width">24dp</dimen>
</resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 5793bbe..2bbaf9c 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -249,6 +249,7 @@
],
srcs: [
"src/android/app/ActivityManagerTest.java",
+ "src/android/colormodel/CamTest.java",
"src/android/content/ContextTest.java",
"src/android/content/pm/PackageManagerTest.java",
"src/android/content/pm/UserInfoTest.java",
diff --git a/core/tests/coretests/src/android/colormodel/CamTest.java b/core/tests/coretests/src/android/colormodel/CamTest.java
index 05fc0e0..cf398db 100644
--- a/core/tests/coretests/src/android/colormodel/CamTest.java
+++ b/core/tests/coretests/src/android/colormodel/CamTest.java
@@ -18,9 +18,12 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.LargeTest;
import org.junit.Assert;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -35,6 +38,9 @@
static final int GREEN = 0xff00ff00;
static final int BLUE = 0xff0000ff;
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void camFromIntToInt() {
Cam cam = Cam.fromInt(RED);
diff --git a/core/tests/resourceflaggingtests/Android.bp b/core/tests/resourceflaggingtests/Android.bp
index dd86094..efb8437 100644
--- a/core/tests/resourceflaggingtests/Android.bp
+++ b/core/tests/resourceflaggingtests/Android.bp
@@ -22,54 +22,6 @@
default_team: "trendy_team_android_resources",
}
-genrule {
- name: "resource-flagging-test-app-resources-compile",
- tools: ["aapt2"],
- srcs: [
- "flagged_resources_res/values/bools.xml",
- ],
- out: ["values_bools.arsc.flat"],
- cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
- "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
-}
-
-genrule {
- name: "resource-flagging-test-app-resources-compile2",
- tools: ["aapt2"],
- srcs: [
- "flagged_resources_res/values/bools2.xml",
- ],
- out: ["values_bools2.arsc.flat"],
- cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
- "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
-}
-
-genrule {
- name: "resource-flagging-test-app-apk",
- tools: ["aapt2"],
- // The first input file in the list must be the manifest
- srcs: [
- "TestAppAndroidManifest.xml",
- ":resource-flagging-test-app-resources-compile",
- ":resource-flagging-test-app-resources-compile2",
- ],
- out: ["resapp.apk"],
- cmd: "$(location aapt2) link -o $(out) --manifest $(in)",
-}
-
-java_genrule {
- name: "resource-flagging-apk-as-resource",
- srcs: [
- ":resource-flagging-test-app-apk",
- ],
- out: ["apks_as_resources.res.zip"],
- tools: ["soong_zip"],
-
- cmd: "mkdir -p $(genDir)/res/raw && " +
- "cp $(in) $(genDir)/res/raw/$$(basename $(in)) && " +
- "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
-}
-
android_test {
name: "ResourceFlaggingTests",
srcs: [
@@ -82,6 +34,6 @@
"testng",
"compatibility-device-util-axt",
],
- resource_zips: [":resource-flagging-apk-as-resource"],
+ resource_zips: [":resource-flagging-test-app-apk-as-resource"],
test_suites: ["device-tests"],
}
diff --git a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
index ad8542e..c1e3578 100644
--- a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
+++ b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
@@ -69,11 +69,23 @@
}
private boolean getBoolean(String name) {
- int resId = mResources.getIdentifier(name, "bool", "com.android.intenal.flaggedresources");
+ int resId = mResources.getIdentifier(
+ name,
+ "bool",
+ "com.android.intenal.flaggedresources");
assertThat(resId).isNotEqualTo(0);
return mResources.getBoolean(resId);
}
+ private String getString(String name) {
+ int resId = mResources.getIdentifier(
+ name,
+ "string",
+ "com.android.intenal.flaggedresources");
+ assertThat(resId).isNotEqualTo(0);
+ return mResources.getString(resId);
+ }
+
private String extractApkAndGetPath(int id) throws Exception {
final Resources resources = mContext.getResources();
try (InputStream is = resources.openRawResource(id)) {
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index e9a08ae..97f1d5e 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -27,7 +27,11 @@
import android.os.Parcel;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -36,6 +40,9 @@
public class PrimitiveSegmentTest {
private static final float TOLERANCE = 1e-2f;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
public void testCreation() {
PrimitiveSegment primitive = new PrimitiveSegment(
@@ -87,7 +94,8 @@
}
@Test
- public void testScale_fullPrimitiveScaleValue() {
+ @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withLegacyScaling_fullPrimitiveScaleValue() {
PrimitiveSegment initial = new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0);
@@ -102,7 +110,24 @@
}
@Test
- public void testScale_halfPrimitiveScaleValue() {
+ @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withScalingV2_fullPrimitiveScaleValue() {
+ PrimitiveSegment initial = new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0);
+
+ assertEquals(1f, initial.scale(1).getScale(), TOLERANCE);
+ assertEquals(0.5f, initial.scale(0.5f).getScale(), TOLERANCE);
+ // The original value was not scaled up, so this only scales it down.
+ assertEquals(1f, initial.scale(1.5f).getScale(), TOLERANCE);
+ assertEquals(2 / 3f, initial.scale(1.5f).scale(2 / 3f).getScale(), TOLERANCE);
+ // Does not restore to the exact original value because scale up is a bit offset.
+ assertEquals(0.8f, initial.scale(0.8f).getScale(), TOLERANCE);
+ assertEquals(0.86f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withLegacyScaling_halfPrimitiveScaleValue() {
PrimitiveSegment initial = new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 0);
@@ -117,6 +142,22 @@
}
@Test
+ @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withScalingV2_halfPrimitiveScaleValue() {
+ PrimitiveSegment initial = new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 0);
+
+ assertEquals(0.5f, initial.scale(1).getScale(), TOLERANCE);
+ assertEquals(0.25f, initial.scale(0.5f).getScale(), TOLERANCE);
+ // The original value was not scaled up, so this only scales it down.
+ assertEquals(0.66f, initial.scale(1.5f).getScale(), TOLERANCE);
+ assertEquals(0.44f, initial.scale(1.5f).scale(2 / 3f).getScale(), TOLERANCE);
+ // Does not restore to the exact original value because scale up is a bit offset.
+ assertEquals(0.4f, initial.scale(0.8f).getScale(), TOLERANCE);
+ assertEquals(0.48f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE);
+ }
+
+ @Test
public void testScale_zeroPrimitiveScaleValue() {
PrimitiveSegment initial = new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_CLICK, 0, 0);
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index 01013ab..bea8293 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -29,7 +29,11 @@
import android.os.Parcel;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -38,6 +42,9 @@
public class RampSegmentTest {
private static final float TOLERANCE = 1e-2f;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
public void testCreation() {
RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
@@ -97,14 +104,18 @@
}
@Test
- public void testScale() {
- RampSegment initial = new RampSegment(0, 1, 0, 0, 0);
+ @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withLegacyScaling_halfAndFullAmplitudes() {
+ RampSegment initial = new RampSegment(0.5f, 1, 0, 0, 0);
- assertEquals(0f, initial.scale(1).getStartAmplitude(), TOLERANCE);
- assertEquals(0f, initial.scale(0.5f).getStartAmplitude(), TOLERANCE);
- assertEquals(0f, initial.scale(1.5f).getStartAmplitude(), TOLERANCE);
- assertEquals(0f, initial.scale(1.5f).scale(2 / 3f).getStartAmplitude(), TOLERANCE);
- assertEquals(0f, initial.scale(0.8f).scale(1.25f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scale(1).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.17f, initial.scale(0.5f).getStartAmplitude(), TOLERANCE);
+ // The original value was not scaled up, so this only scales it down.
+ assertEquals(0.86f, initial.scale(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.47f, initial.scale(1.5f).scale(2 / 3f).getStartAmplitude(), TOLERANCE);
+ // Does not restore to the exact original value because scale up is a bit offset.
+ assertEquals(0.35f, initial.scale(0.8f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scale(0.8f).scale(1.25f).getStartAmplitude(), TOLERANCE);
assertEquals(1f, initial.scale(1).getEndAmplitude(), TOLERANCE);
assertEquals(0.34f, initial.scale(0.5f).getEndAmplitude(), TOLERANCE);
@@ -117,17 +128,38 @@
}
@Test
- public void testScale_halfPrimitiveScaleValue() {
+ @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withScalingV2_halfAndFullAmplitudes() {
RampSegment initial = new RampSegment(0.5f, 1, 0, 0, 0);
assertEquals(0.5f, initial.scale(1).getStartAmplitude(), TOLERANCE);
- assertEquals(0.17f, initial.scale(0.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.25f, initial.scale(0.5f).getStartAmplitude(), TOLERANCE);
+ // The original value was not scaled up, so this only scales it down.
+ assertEquals(0.66f, initial.scale(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.44f, initial.scale(1.5f).scale(2 / 3f).getStartAmplitude(), TOLERANCE);
// Does not restore to the exact original value because scale up is a bit offset.
- assertEquals(0.86f, initial.scale(1.5f).getStartAmplitude(), TOLERANCE);
- assertEquals(0.47f, initial.scale(1.5f).scale(2 / 3f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.4f, initial.scale(0.8f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.48f, initial.scale(0.8f).scale(1.25f).getStartAmplitude(), TOLERANCE);
+
+ assertEquals(1f, initial.scale(1).getEndAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scale(0.5f).getEndAmplitude(), TOLERANCE);
+ // The original value was not scaled up, so this only scales it down.
+ assertEquals(1f, initial.scale(1.5f).getEndAmplitude(), TOLERANCE);
+ assertEquals(2 / 3f, initial.scale(1.5f).scale(2 / 3f).getEndAmplitude(), TOLERANCE);
// Does not restore to the exact original value because scale up is a bit offset.
- assertEquals(0.35f, initial.scale(0.8f).getStartAmplitude(), TOLERANCE);
- assertEquals(0.5f, initial.scale(0.8f).scale(1.25f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.81f, initial.scale(0.8f).getEndAmplitude(), TOLERANCE);
+ assertEquals(0.86f, initial.scale(0.8f).scale(1.25f).getEndAmplitude(), TOLERANCE);
+ }
+
+ @Test
+ public void testScale_zeroAmplitude() {
+ RampSegment initial = new RampSegment(0, 1, 0, 0, 0);
+
+ assertEquals(0f, initial.scale(1).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scale(0.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scale(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scale(1.5f).scale(2 / 3f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scale(0.8f).scale(1.25f).getStartAmplitude(), TOLERANCE);
}
@Test
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 40776ab..411074a 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -27,7 +27,11 @@
import android.os.Parcel;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -35,6 +39,10 @@
@RunWith(JUnit4.class)
public class StepSegmentTest {
private static final float TOLERANCE = 1e-2f;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
public void testCreation() {
StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f,
@@ -93,7 +101,8 @@
}
@Test
- public void testScale_fullAmplitude() {
+ @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withLegacyScaling_fullAmplitude() {
StepSegment initial = new StepSegment(1f, 0, 0);
assertEquals(1f, initial.scale(1).getAmplitude(), TOLERANCE);
@@ -107,7 +116,23 @@
}
@Test
- public void testScale_halfAmplitude() {
+ @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withScalingV2_fullAmplitude() {
+ StepSegment initial = new StepSegment(1f, 0, 0);
+
+ assertEquals(1f, initial.scale(1).getAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scale(0.5f).getAmplitude(), TOLERANCE);
+ // The original value was not scaled up, so this only scales it down.
+ assertEquals(1f, initial.scale(1.5f).getAmplitude(), TOLERANCE);
+ assertEquals(2 / 3f, initial.scale(1.5f).scale(2 / 3f).getAmplitude(), TOLERANCE);
+ // Does not restore to the exact original value because scale up is a bit offset.
+ assertEquals(0.8f, initial.scale(0.8f).getAmplitude(), TOLERANCE);
+ assertEquals(0.86f, initial.scale(0.8f).scale(1.25f).getAmplitude(), TOLERANCE);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withLegacyScaling_halfAmplitude() {
StepSegment initial = new StepSegment(0.5f, 0, 0);
assertEquals(0.5f, initial.scale(1).getAmplitude(), TOLERANCE);
@@ -121,6 +146,21 @@
}
@Test
+ @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testScale_withScalingV2_halfAmplitude() {
+ StepSegment initial = new StepSegment(0.5f, 0, 0);
+
+ assertEquals(0.5f, initial.scale(1).getAmplitude(), TOLERANCE);
+ assertEquals(0.25f, initial.scale(0.5f).getAmplitude(), TOLERANCE);
+ // The original value was not scaled up, so this only scales it down.
+ assertEquals(0.66f, initial.scale(1.5f).getAmplitude(), TOLERANCE);
+ assertEquals(0.44f, initial.scale(1.5f).scale(2 / 3f).getAmplitude(), TOLERANCE);
+ // Does not restore to the exact original value because scale up is a bit offset.
+ assertEquals(0.4f, initial.scale(0.8f).getAmplitude(), TOLERANCE);
+ assertEquals(0.48f, initial.scale(0.8f).scale(1.25f).getAmplitude(), TOLERANCE);
+ }
+
+ @Test
public void testScale_zeroAmplitude() {
StepSegment initial = new StepSegment(0, 0, 0);
diff --git a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index bf9a820..1cc38de 100644
--- a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -24,22 +24,32 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
+import android.os.PersistableBundle;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Xml;
import com.android.modules.utils.TypedXmlPullParser;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.xmlpull.v1.XmlPullParser;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
+import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -53,6 +63,9 @@
@RunWith(JUnit4.class)
public class VibrationEffectXmlSerializationTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
public void isSupportedMimeType_onlySupportsVibrationXmlMimeType() {
// Single MIME type supported
@@ -422,6 +435,97 @@
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void testVendorEffect_featureFlagEnabled_allSucceed() throws Exception {
+ PersistableBundle vendorData = new PersistableBundle();
+ vendorData.putInt("id", 1);
+ vendorData.putDouble("scale", 0.5);
+ vendorData.putBoolean("loop", false);
+ vendorData.putLongArray("amplitudes", new long[] { 0, 255, 128 });
+ vendorData.putString("label", "vibration");
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ vendorData.writeToStream(outputStream);
+ String vendorDataStr = Base64.getEncoder().encodeToString(outputStream.toByteArray());
+
+ VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData);
+ String xml = "<vibration-effect><vendor-effect> " // test trailing whitespace is ignored
+ + vendorDataStr
+ + " \n </vendor-effect></vibration-effect>";
+
+ assertPublicApisParserSucceeds(xml, effect);
+ assertPublicApisSerializerSucceeds(effect, vendorDataStr);
+ assertPublicApisRoundTrip(effect);
+
+ assertHiddenApisParserSucceeds(xml, effect);
+ assertHiddenApisSerializerSucceeds(effect, vendorDataStr);
+ assertHiddenApisRoundTrip(effect);
+
+ // Check PersistableBundle from round-trip
+ PersistableBundle parsedVendorData =
+ ((VibrationEffect.VendorEffect) parseVibrationEffect(serialize(effect),
+ /* flags= */ 0)).getVendorData();
+ assertThat(parsedVendorData.size()).isEqualTo(vendorData.size());
+ assertThat(parsedVendorData.getInt("id")).isEqualTo(1);
+ assertThat(parsedVendorData.getDouble("scale")).isEqualTo(0.5);
+ assertThat(parsedVendorData.getBoolean("loop")).isFalse();
+ assertArrayEquals(parsedVendorData.getLongArray("amplitudes"), new long[] { 0, 255, 128 });
+ assertThat(parsedVendorData.getString("label")).isEqualTo("vibration");
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void testInvalidVendorEffect_featureFlagEnabled_allFail() throws IOException {
+ String emptyTag = "<vibration-effect><vendor-effect/></vibration-effect>";
+ assertPublicApisParserFails(emptyTag);
+ assertHiddenApisParserFails(emptyTag);
+
+ String emptyStringTag =
+ "<vibration-effect><vendor-effect> \n </vendor-effect></vibration-effect>";
+ assertPublicApisParserFails(emptyStringTag);
+ assertHiddenApisParserFails(emptyStringTag);
+
+ String invalidString =
+ "<vibration-effect><vendor-effect>invalid</vendor-effect></vibration-effect>";
+ assertPublicApisParserFails(invalidString);
+ assertHiddenApisParserFails(invalidString);
+
+ String validBase64String =
+ "<vibration-effect><vendor-effect>c29tZXNh</vendor-effect></vibration-effect>";
+ assertPublicApisParserFails(validBase64String);
+ assertHiddenApisParserFails(validBase64String);
+
+ PersistableBundle emptyData = new PersistableBundle();
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ emptyData.writeToStream(outputStream);
+ String emptyBundleString = "<vibration-effect><vendor-effect>"
+ + Base64.getEncoder().encodeToString(outputStream.toByteArray())
+ + "</vendor-effect></vibration-effect>";
+ assertPublicApisParserFails(emptyBundleString);
+ assertHiddenApisParserFails(emptyBundleString);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void testVendorEffect_featureFlagDisabled_allFail() throws Exception {
+ PersistableBundle vendorData = new PersistableBundle();
+ vendorData.putInt("id", 1);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ vendorData.writeToStream(outputStream);
+ String vendorDataStr = Base64.getEncoder().encodeToString(outputStream.toByteArray());
+ String xml = "<vibration-effect><vendor-effect>"
+ + vendorDataStr
+ + "</vendor-effect></vibration-effect>";
+ VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData);
+
+ assertPublicApisParserFails(xml);
+ assertPublicApisSerializerFails(vendorEffect);
+
+ assertHiddenApisParserFails(xml);
+ assertHiddenApisSerializerFails(vendorEffect);
+ }
+
private void assertPublicApisParserFails(String xml) {
assertThrows("Expected parseVibrationEffect to fail for " + xml,
VibrationXmlParser.ParseFailedException.class,
@@ -493,6 +597,12 @@
() -> serialize(effect));
}
+ private void assertHiddenApisSerializerFails(VibrationEffect effect) {
+ assertThrows("Expected serialization to fail for " + effect,
+ VibrationXmlSerializer.SerializationFailedException.class,
+ () -> serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS));
+ }
+
private void assertPublicApisSerializerSucceeds(VibrationEffect effect,
String... expectedSegments) throws Exception {
assertSerializationContainsSegments(serialize(effect), expectedSegments);
diff --git a/core/xsd/vibrator/vibration/schema/current.txt b/core/xsd/vibrator/vibration/schema/current.txt
index f0e13c4..280b405 100644
--- a/core/xsd/vibrator/vibration/schema/current.txt
+++ b/core/xsd/vibrator/vibration/schema/current.txt
@@ -41,9 +41,11 @@
ctor public VibrationEffect();
method public com.android.internal.vibrator.persistence.PredefinedEffect getPredefinedEffect_optional();
method public com.android.internal.vibrator.persistence.PrimitiveEffect getPrimitiveEffect_optional();
+ method public byte[] getVendorEffect_optional();
method public com.android.internal.vibrator.persistence.WaveformEffect getWaveformEffect_optional();
method public void setPredefinedEffect_optional(com.android.internal.vibrator.persistence.PredefinedEffect);
method public void setPrimitiveEffect_optional(com.android.internal.vibrator.persistence.PrimitiveEffect);
+ method public void setVendorEffect_optional(byte[]);
method public void setWaveformEffect_optional(com.android.internal.vibrator.persistence.WaveformEffect);
}
diff --git a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
index fcd250b..21a6fac 100644
--- a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
+++ b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
@@ -46,6 +46,9 @@
<!-- Predefined vibration effect -->
<xs:element name="predefined-effect" type="PredefinedEffect"/>
+ <!-- Vendor vibration effect -->
+ <xs:element name="vendor-effect" type="VendorEffect"/>
+
<!-- Primitive composition effect -->
<xs:sequence>
<xs:element name="primitive-effect" type="PrimitiveEffect"/>
@@ -136,6 +139,10 @@
</xs:restriction>
</xs:simpleType>
+ <xs:simpleType name="VendorEffect">
+ <xs:restriction base="xs:base64Binary"/>
+ </xs:simpleType>
+
<xs:complexType name="PrimitiveEffect">
<xs:attribute name="name" type="PrimitiveEffectName" use="required"/>
<xs:attribute name="scale" type="PrimitiveScale"/>
diff --git a/core/xsd/vibrator/vibration/vibration.xsd b/core/xsd/vibrator/vibration/vibration.xsd
index b9de691..d35d777 100644
--- a/core/xsd/vibrator/vibration/vibration.xsd
+++ b/core/xsd/vibrator/vibration/vibration.xsd
@@ -44,6 +44,9 @@
<!-- Predefined vibration effect -->
<xs:element name="predefined-effect" type="PredefinedEffect"/>
+ <!-- Vendor vibration effect -->
+ <xs:element name="vendor-effect" type="VendorEffect"/>
+
<!-- Primitive composition effect -->
<xs:sequence>
<xs:element name="primitive-effect" type="PrimitiveEffect"/>
@@ -113,6 +116,10 @@
</xs:restriction>
</xs:simpleType>
+ <xs:simpleType name="VendorEffect">
+ <xs:restriction base="xs:base64Binary"/>
+ </xs:simpleType>
+
<xs:complexType name="PrimitiveEffect">
<xs:attribute name="name" type="PrimitiveEffectName" use="required"/>
<xs:attribute name="scale" type="PrimitiveScale"/>
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 5135e9e..1c3d9c3 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -166,6 +166,16 @@
},
}
+java_library {
+ name: "WindowManager-Shell-lite-proto",
+
+ srcs: ["src/com/android/wm/shell/desktopmode/education/data/proto/**/*.proto"],
+
+ proto: {
+ type: "lite",
+ },
+}
+
filegroup {
name: "wm_shell-shared-aidls",
@@ -215,6 +225,7 @@
"androidx.core_core-animation",
"androidx.core_core-ktx",
"androidx.arch.core_core-runtime",
+ "androidx.datastore_datastore",
"androidx.compose.material3_material3",
"androidx-constraintlayout_constraintlayout",
"androidx.dynamicanimation_dynamicanimation",
@@ -225,6 +236,7 @@
"//frameworks/libs/systemui:iconloader_base",
"com_android_wm_shell_flags_lib",
"WindowManager-Shell-proto",
+ "WindowManager-Shell-lite-proto",
"WindowManager-Shell-shared",
"perfetto_trace_java_protos",
"dagger2",
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 0a8166f..df5f1e4 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -139,6 +139,10 @@
<string name="bubble_accessibility_action_move_bottom_left">Move bottom left</string>
<!-- Action in accessibility menu to move the stack of bubbles to the bottom right of the screen. [CHAR LIMIT=30]-->
<string name="bubble_accessibility_action_move_bottom_right">Move bottom right</string>
+ <!-- Click action label for bubbles to expand menu. [CHAR LIMIT=30]-->
+ <string name="bubble_accessibility_action_expand_menu">expand menu</string>
+ <!-- Click action label for bubbles to collapse menu. [CHAR LIMIT=30]-->
+ <string name="bubble_accessibility_action_collapse_menu">collapse menu</string>
<!-- Accessibility announcement when the stack of bubbles expands. [CHAR LIMIT=NONE]-->
<string name="bubble_accessibility_announce_expand">expand <xliff:g id="bubble_title" example="Messages">%1$s</xliff:g></string>
<!-- Accessibility announcement when the stack of bubbles collapses. [CHAR LIMIT=NONE]-->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 24c568c..b7834db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -32,6 +32,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import com.android.wm.shell.R;
@@ -188,12 +189,30 @@
// Handle view needs to draw on top of task view.
bringChildToFront(mHandleView);
+
+ mHandleView.setAccessibilityDelegate(new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK, getResources().getString(
+ R.string.bubble_accessibility_action_expand_menu)));
+ }
+ });
}
mMenuViewController = new BubbleBarMenuViewController(mContext, this);
mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() {
@Override
public void onMenuVisibilityChanged(boolean visible) {
setObscured(visible);
+ if (visible) {
+ mHandleView.setFocusable(false);
+ mHandleView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ } else {
+ mHandleView.setFocusable(true);
+ mHandleView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index 8389c81..0300869 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -23,7 +23,9 @@
import android.graphics.drawable.Icon;
import android.util.AttributeSet;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -71,6 +73,16 @@
mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title);
mBubbleDismissIconView = findViewById(R.id.bubble_bar_manage_menu_dismiss_icon);
updateThemeColors();
+
+ mBubbleSectionView.setAccessibilityDelegate(new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK, getResources().getString(
+ R.string.bubble_accessibility_action_collapse_menu)));
+ }
+ });
}
private void updateThemeColors() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index b8b62a7..e787a3d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -72,6 +72,7 @@
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator;
import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.draganddrop.GlobalDragListener;
import com.android.wm.shell.freeform.FreeformComponents;
@@ -679,6 +680,13 @@
return new DesktopModeEventLogger();
}
+ @WMSingleton
+ @Provides
+ static AppHandleEducationDatastoreRepository provideAppHandleEducationDatastoreRepository(
+ Context context) {
+ return new AppHandleEducationDatastoreRepository(context);
+ }
+
//
// Drag and drop
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 400882a..05c9d02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -73,25 +73,9 @@
sessionId,
taskUpdate.instanceId
)
- FrameworkStatsLog.write(
- DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
- /* task_event */
+ logTaskUpdate(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED,
- /* instance_id */
- taskUpdate.instanceId,
- /* uid */
- taskUpdate.uid,
- /* task_height */
- taskUpdate.taskHeight,
- /* task_width */
- taskUpdate.taskWidth,
- /* task_x */
- taskUpdate.taskX,
- /* task_y */
- taskUpdate.taskY,
- /* session_id */
- sessionId
- )
+ sessionId, taskUpdate)
}
/**
@@ -105,25 +89,9 @@
sessionId,
taskUpdate.instanceId
)
- FrameworkStatsLog.write(
- DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
- /* task_event */
+ logTaskUpdate(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED,
- /* instance_id */
- taskUpdate.instanceId,
- /* uid */
- taskUpdate.uid,
- /* task_height */
- taskUpdate.taskHeight,
- /* task_width */
- taskUpdate.taskWidth,
- /* task_x */
- taskUpdate.taskX,
- /* task_y */
- taskUpdate.taskY,
- /* session_id */
- sessionId
- )
+ sessionId, taskUpdate)
}
/**
@@ -137,10 +105,16 @@
sessionId,
taskUpdate.instanceId
)
+ logTaskUpdate(
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
+ sessionId, taskUpdate)
+ }
+
+ private fun logTaskUpdate(taskEvent: Int, sessionId: Int, taskUpdate: TaskUpdate) {
FrameworkStatsLog.write(
DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
/* task_event */
- FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
+ taskEvent,
/* instance_id */
taskUpdate.instanceId,
/* uid */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 38675129..597637d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -70,11 +70,11 @@
// TODO(b/333018485): replace this observer when implementing the minimize-animation
private inner class MinimizeTransitionObserver : TransitionObserver {
- private val mPendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
- private val mActiveTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
+ private val pendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
+ private val activeTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
fun addPendingTransitionToken(transition: IBinder, taskDetails: TaskDetails) {
- mPendingTransitionTokensAndTasks[transition] = taskDetails
+ pendingTransitionTokensAndTasks[transition] = taskDetails
}
override fun onTransitionReady(
@@ -83,9 +83,7 @@
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction
) {
- val taskToMinimize = mPendingTransitionTokensAndTasks.remove(transition) ?: return
- taskToMinimize.transitionInfo = info
- mActiveTransitionTokensAndTasks[transition] = taskToMinimize
+ val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return
if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return
@@ -97,6 +95,8 @@
return
}
+ taskToMinimize.transitionInfo = info
+ activeTransitionTokensAndTasks[transition] = taskToMinimize
this@DesktopTasksLimiter.markTaskMinimized(
taskToMinimize.displayId, taskToMinimize.taskId)
}
@@ -121,7 +121,7 @@
}
override fun onTransitionStarting(transition: IBinder) {
- val mActiveTaskDetails = mActiveTransitionTokensAndTasks[transition]
+ val mActiveTaskDetails = activeTransitionTokensAndTasks[transition]
if (mActiveTaskDetails != null && mActiveTaskDetails.transitionInfo != null) {
// Begin minimize window CUJ instrumentation.
interactionJankMonitor.begin(
@@ -132,11 +132,11 @@
}
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
- if (mActiveTransitionTokensAndTasks.remove(merged) != null) {
+ if (activeTransitionTokensAndTasks.remove(merged) != null) {
interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
}
- mPendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
- mPendingTransitionTokensAndTasks[playing] = taskToTransfer
+ pendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
+ pendingTransitionTokensAndTasks[playing] = taskToTransfer
}
}
@@ -144,14 +144,14 @@
ProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"DesktopTasksLimiter: transition %s finished", transition)
- if (mActiveTransitionTokensAndTasks.remove(transition) != null) {
+ if (activeTransitionTokensAndTasks.remove(transition) != null) {
if (aborted) {
interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
} else {
interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
}
}
- mPendingTransitionTokensAndTasks.remove(transition)
+ pendingTransitionTokensAndTasks.remove(transition)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
new file mode 100644
index 0000000..bf4a2ab
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 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.wm.shell.desktopmode.education.data
+
+import android.content.Context
+import android.util.Log
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.core.Serializer
+import androidx.datastore.dataStore
+import androidx.datastore.dataStoreFile
+import com.android.framework.protobuf.InvalidProtocolBufferException
+import com.android.internal.annotations.VisibleForTesting
+import java.io.InputStream
+import java.io.OutputStream
+import kotlinx.coroutines.flow.first
+
+/**
+ * Manages interactions with the App Handle education datastore.
+ *
+ * This class provides a layer of abstraction between the UI/business logic and the underlying
+ * DataStore.
+ */
+class AppHandleEducationDatastoreRepository
+@VisibleForTesting
+constructor(private val dataStore: DataStore<WindowingEducationProto>) {
+ constructor(
+ context: Context
+ ) : this(
+ DataStoreFactory.create(
+ serializer = WindowingEducationProtoSerializer,
+ produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) }))
+
+ /**
+ * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the
+ * DataStore is empty or there's an error reading, it returns the default value of Proto.
+ */
+ suspend fun windowingEducationProto(): WindowingEducationProto =
+ try {
+ dataStore.data.first()
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to read from datastore")
+ WindowingEducationProto.getDefaultInstance()
+ }
+
+ companion object {
+ private const val TAG = "AppHandleEducationDatastoreRepository"
+ private const val APP_HANDLE_EDUCATION_DATASTORE_FILEPATH = "app_handle_education.pb"
+
+ object WindowingEducationProtoSerializer : Serializer<WindowingEducationProto> {
+
+ override val defaultValue: WindowingEducationProto =
+ WindowingEducationProto.getDefaultInstance()
+
+ override suspend fun readFrom(input: InputStream): WindowingEducationProto =
+ try {
+ WindowingEducationProto.parseFrom(input)
+ } catch (exception: InvalidProtocolBufferException) {
+ throw CorruptionException("Cannot read proto.", exception)
+ }
+
+ override suspend fun writeTo(windowingProto: WindowingEducationProto, output: OutputStream) =
+ windowingProto.writeTo(output)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
new file mode 100644
index 0000000..d29ec53
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+syntax = "proto2";
+
+option java_package = "com.android.wm.shell.desktopmode.education.data";
+option java_multiple_files = true;
+
+// Desktop Windowing education data
+message WindowingEducationProto {
+ // Timestamp in milliseconds of when the education was last viewed.
+ optional int64 education_viewed_timestamp_millis = 1;
+ // Timestamp in milliseconds of when the feature was last used.
+ optional int64 feature_used_timestamp_millis = 2;
+ oneof education_data {
+ // Fields specific to app handle education
+ AppHandleEducation app_handle_education = 3;
+ }
+
+ message AppHandleEducation {
+ // Map that stores app launch count for corresponding package
+ map<string, int32> app_usage_stats = 1;
+ // Timestamp of when app_usage_stats was last cached
+ optional int64 app_usage_stats_last_update_timestamp_millis = 2;
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 723a531..428cc91 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -693,16 +693,6 @@
return;
}
- if (mSplitScreenOptional.isPresent()) {
- // If pip activity will reparent to origin task case and if the origin task still
- // under split root, apply exit split transaction to make it expand to fullscreen.
- SplitScreenController split = mSplitScreenOptional.get();
- if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) {
- split.prepareExitSplitScreen(wct, split.getStageOfTask(
- mTaskInfo.lastParentTaskIdBeforePip),
- SplitScreenController.EXIT_REASON_APP_FINISHED);
- }
- }
mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 7774384..dc21f82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -31,6 +31,8 @@
import android.os.Bundle;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.window.DisplayAreaInfo;
+import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
@@ -40,6 +42,7 @@
import com.android.internal.util.Preconditions;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
@@ -71,7 +74,8 @@
*/
public class PipController implements ConfigurationChangeListener,
PipTransitionState.PipTransitionStateChangedListener,
- DisplayController.OnDisplaysChangedListener, RemoteCallable<PipController> {
+ DisplayController.OnDisplaysChangedListener,
+ DisplayChangeController.OnDisplayChangingListener, RemoteCallable<PipController> {
private static final String TAG = PipController.class.getSimpleName();
private static final String SWIPE_TO_PIP_APP_BOUNDS = "pip_app_bounds";
private static final String SWIPE_TO_PIP_OVERLAY = "swipe_to_pip_overlay";
@@ -197,11 +201,12 @@
mPipDisplayLayoutState.setDisplayLayout(layout);
mDisplayController.addDisplayWindowListener(this);
+ mDisplayController.addDisplayChangingController(this);
mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
new DisplayInsetsController.OnInsetsChangedListener() {
@Override
public void insetsChanged(InsetsState insetsState) {
- onDisplayChanged(mDisplayController
+ setDisplayLayout(mDisplayController
.getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
}
});
@@ -264,11 +269,12 @@
@Override
public void onThemeChanged() {
- onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()));
+ setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
}
//
- // DisplayController.OnDisplaysChangedListener implementations
+ // DisplayController.OnDisplaysChangedListener and
+ // DisplayChangeController.OnDisplayChangingListener implementations
//
@Override
@@ -276,7 +282,7 @@
if (displayId != mPipDisplayLayoutState.getDisplayId()) {
return;
}
- onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
}
@Override
@@ -284,10 +290,35 @@
if (displayId != mPipDisplayLayoutState.getDisplayId()) {
return;
}
- onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
}
- private void onDisplayChanged(DisplayLayout layout) {
+ /**
+ * A callback for any observed transition that contains a display change in its
+ * {@link android.window.TransitionRequestInfo} with a non-zero rotation delta.
+ */
+ @Override
+ public void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction t) {
+ if (!mPipTransitionState.isInPip()) {
+ return;
+ }
+
+ // Calculate the snap fraction pre-rotation.
+ float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds());
+
+ // Update the caches to reflect the new display layout and movement bounds.
+ mPipDisplayLayoutState.rotateTo(toRotation);
+ mPipTouchHandler.updateMovementBounds();
+
+ // The policy is to keep PiP width, height and snap fraction invariant.
+ Rect toBounds = mPipBoundsState.getBounds();
+ mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
+ mPipBoundsState.setBounds(toBounds);
+ t.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
+ }
+
+ private void setDisplayLayout(DisplayLayout layout) {
mPipDisplayLayoutState.setDisplayLayout(layout);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index d7c225b..d75fa00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -1081,7 +1081,7 @@
* Updates the current movement bounds based on whether the menu is currently visible and
* resized.
*/
- private void updateMovementBounds() {
+ void updateMovementBounds() {
Rect insetBounds = new Rect();
mPipBoundsAlgorithm.getInsetBounds(insetBounds);
mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 48d17ec6..c11a112 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -439,9 +439,9 @@
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition: hasPendingResize=%b",
mPendingResize != null);
if (mPendingResize != null) {
+ mPendingResize.cancel(null);
mainDecor.cancelRunningAnimations();
sideDecor.cancelRunningAnimations();
- mPendingResize.cancel(null);
mAnimations.clear();
onFinish(null /* wct */);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index a040865..4d761e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -44,6 +44,8 @@
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
+ "androidx.datastore_datastore",
+ "kotlinx_coroutines_test",
"androidx.dynamicanimation_dynamicanimation",
"dagger2",
"frameworks-base-testutils",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
new file mode 100644
index 0000000..4d40738
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 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.wm.shell.desktopmode.education
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.dataStoreFile
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
+import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
+class AppHandleEducationDatastoreRepositoryTest {
+ private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
+ private lateinit var testDatastore: DataStore<WindowingEducationProto>
+ private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
+ private lateinit var datastoreScope: CoroutineScope
+
+ @Before
+ fun setUp() {
+ Dispatchers.setMain(StandardTestDispatcher())
+ datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+ testDatastore =
+ DataStoreFactory.create(
+ serializer =
+ AppHandleEducationDatastoreRepository.Companion.WindowingEducationProtoSerializer,
+ scope = datastoreScope) {
+ testContext.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE)
+ }
+ datastoreRepository = AppHandleEducationDatastoreRepository(testDatastore)
+ }
+
+ @After
+ fun tearDown() {
+ File(ApplicationProvider.getApplicationContext<Context>().filesDir, "datastore")
+ .deleteRecursively()
+
+ datastoreScope.cancel()
+ }
+
+ @Test
+ fun getWindowingEducationProto_returnsCorrectProto() =
+ runTest(StandardTestDispatcher()) {
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ educationViewedTimestampMillis = 123L,
+ featureUsedTimestampMillis = 124L,
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+ appUsageStatsLastUpdateTimestampMillis = 125L)
+ testDatastore.updateData { windowingEducationProto }
+
+ val resultProto = datastoreRepository.windowingEducationProto()
+
+ assertThat(resultProto).isEqualTo(windowingEducationProto)
+ }
+
+ private fun createWindowingEducationProto(
+ educationViewedTimestampMillis: Long? = null,
+ featureUsedTimestampMillis: Long? = null,
+ appUsageStats: Map<String, Int>? = null,
+ appUsageStatsLastUpdateTimestampMillis: Long? = null
+ ): WindowingEducationProto =
+ WindowingEducationProto.newBuilder()
+ .apply {
+ if (educationViewedTimestampMillis != null)
+ setEducationViewedTimestampMillis(educationViewedTimestampMillis)
+ if (featureUsedTimestampMillis != null)
+ setFeatureUsedTimestampMillis(featureUsedTimestampMillis)
+ setAppHandleEducation(
+ createAppHandleEducationProto(
+ appUsageStats, appUsageStatsLastUpdateTimestampMillis))
+ }
+ .build()
+
+ private fun createAppHandleEducationProto(
+ appUsageStats: Map<String, Int>? = null,
+ appUsageStatsLastUpdateTimestampMillis: Long? = null
+ ): WindowingEducationProto.AppHandleEducation =
+ WindowingEducationProto.AppHandleEducation.newBuilder()
+ .apply {
+ if (appUsageStats != null) putAllAppUsageStats(appUsageStats)
+ if (appUsageStatsLastUpdateTimestampMillis != null)
+ setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestampMillis)
+ }
+ .build()
+
+ companion object {
+ private const val GMAIL_PACKAGE_NAME = "com.google.android.gm"
+ private const val APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE = "app_handle_education_test.pb"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 6d68797..fa905e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -57,6 +57,7 @@
import android.view.View
import android.view.WindowInsets.Type.statusBars
import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
@@ -356,6 +357,36 @@
}
@Test
+ fun testCloseButtonInFreeform() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val windowDecor = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task)
+ val onClickListenerCaptor = argumentCaptor<View.OnClickListener>()
+ verify(windowDecor).setCaptionListeners(
+ onClickListenerCaptor.capture(), any(), any(), any())
+
+ val onClickListener = onClickListenerCaptor.firstValue
+ val view = mock(View::class.java)
+ whenever(view.id).thenReturn(R.id.close_window)
+
+ val freeformTaskTransitionStarter = mock(FreeformTaskTransitionStarter::class.java)
+ desktopModeWindowDecorViewModel
+ .setFreeformTaskTransitionStarter(freeformTaskTransitionStarter)
+
+ onClickListener.onClick(view)
+
+ val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
+ verify(freeformTaskTransitionStarter).startRemoveTransition(transactionCaptor.capture())
+ val wct = transactionCaptor.firstValue
+
+ assertEquals(1, wct.getHierarchyOps().size)
+ assertEquals(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK,
+ wct.getHierarchyOps().get(0).getType())
+ assertEquals(task.token.asBinder(), wct.getHierarchyOps().get(0).getContainer())
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() {
val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d184f64..1217b47 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -42,6 +42,9 @@
constexpr bool initialize_gl_always() {
return false;
}
+constexpr bool resample_gainmap_regions() {
+ return false;
+}
} // namespace hwui_flags
#endif
@@ -100,6 +103,7 @@
bool Properties::clipSurfaceViews = false;
bool Properties::hdr10bitPlus = false;
+bool Properties::resampleGainmapRegions = false;
int Properties::timeoutMultiplier = 1;
@@ -175,6 +179,8 @@
clipSurfaceViews =
base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews());
hdr10bitPlus = hwui_flags::hdr_10bit_plus();
+ resampleGainmapRegions = base::GetBoolProperty("debug.hwui.resample_gainmap_regions",
+ hwui_flags::resample_gainmap_regions());
timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index e264642..73e80ce 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -342,6 +342,7 @@
static bool clipSurfaceViews;
static bool hdr10bitPlus;
+ static bool resampleGainmapRegions;
static int timeoutMultiplier;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index cd3ae53..13c0b00 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -97,3 +97,13 @@
description: "Initialize GL even when HWUI is set to use Vulkan. This improves app startup time for apps using GL."
bug: "335172671"
}
+
+flag {
+ name: "resample_gainmap_regions"
+ namespace: "core_graphics"
+ description: "Resample gainmaps when decoding regions, to improve visual quality"
+ bug: "352847821"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index ea5c144..6a65b82 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -87,8 +87,17 @@
requireUnpremul, prefColorSpace);
}
- bool decodeGainmapRegion(sp<uirenderer::Gainmap>* outGainmap, int outWidth, int outHeight,
- const SkIRect& desiredSubset, int sampleSize, bool requireUnpremul) {
+ // Decodes the gainmap region. If decoding succeeded, returns true and
+ // populate outGainmap with the decoded gainmap. Otherwise, returns false.
+ //
+ // Note that the desiredSubset is the logical region within the source
+ // gainmap that we want to decode. This is used for scaling into the final
+ // bitmap, since we do not want to include portions of the gainmap outside
+ // of this region. desiredSubset is also _not_ guaranteed to be
+ // pixel-aligned, so it's not possible to simply resize the resulting
+ // bitmap to accomplish this.
+ bool decodeGainmapRegion(sp<uirenderer::Gainmap>* outGainmap, SkISize bitmapDimensions,
+ const SkRect& desiredSubset, int sampleSize, bool requireUnpremul) {
SkColorType decodeColorType = mGainmapBRD->computeOutputColorType(kN32_SkColorType);
sk_sp<SkColorSpace> decodeColorSpace =
mGainmapBRD->computeOutputColorSpace(decodeColorType, nullptr);
@@ -107,13 +116,30 @@
// allocation type. RecyclingClippingPixelAllocator will populate this with the
// actual alpha type in either allocPixelRef() or copyIfNecessary()
sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(SkImageInfo::Make(
- outWidth, outHeight, decodeColorType, kPremul_SkAlphaType, decodeColorSpace));
+ bitmapDimensions, decodeColorType, kPremul_SkAlphaType, decodeColorSpace));
if (!nativeBitmap) {
ALOGE("OOM allocating Bitmap for Gainmap");
return false;
}
- RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false);
- if (!mGainmapBRD->decodeRegion(&bm, &allocator, desiredSubset, sampleSize, decodeColorType,
+
+ // Round out the subset so that we decode a slightly larger region, in
+ // case the subset has fractional components.
+ SkIRect roundedSubset = desiredSubset.roundOut();
+
+ // Map the desired subset to the space of the decoded gainmap. The
+ // subset is repositioned relative to the resulting bitmap, and then
+ // scaled to respect the sampleSize.
+ // This assumes that the subset will not be modified by the decoder, which is true
+ // for existing gainmap formats.
+ SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()),
+ -std::floorf(desiredSubset.top()));
+ logicalSubset.fLeft /= sampleSize;
+ logicalSubset.fTop /= sampleSize;
+ logicalSubset.fRight /= sampleSize;
+ logicalSubset.fBottom /= sampleSize;
+
+ RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false, logicalSubset);
+ if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType,
requireUnpremul, decodeColorSpace)) {
ALOGE("Error decoding Gainmap region");
return false;
@@ -130,16 +156,31 @@
return true;
}
- SkIRect calculateGainmapRegion(const SkIRect& mainImageRegion, int* inOutWidth,
- int* inOutHeight) {
+ struct Projection {
+ SkRect srcRect;
+ SkISize destSize;
+ };
+ Projection calculateGainmapRegion(const SkIRect& mainImageRegion, SkISize dimensions) {
const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width();
const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height();
- *inOutWidth *= scaleX;
- *inOutHeight *= scaleY;
- // TODO: Account for rounding error?
- return SkIRect::MakeLTRB(mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY,
- mainImageRegion.right() * scaleX,
- mainImageRegion.bottom() * scaleY);
+
+ if (uirenderer::Properties::resampleGainmapRegions) {
+ const auto srcRect = SkRect::MakeLTRB(
+ mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY,
+ mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY);
+ // Request a slightly larger destination size so that the gainmap
+ // subset we want fits entirely in this size.
+ const auto destSize = SkISize::Make(std::ceil(dimensions.width() * scaleX),
+ std::ceil(dimensions.height() * scaleY));
+ return Projection{.srcRect = srcRect, .destSize = destSize};
+ } else {
+ const auto srcRect = SkRect::Make(SkIRect::MakeLTRB(
+ mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY,
+ mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY));
+ const auto destSize =
+ SkISize::Make(dimensions.width() * scaleX, dimensions.height() * scaleY);
+ return Projection{.srcRect = srcRect, .destSize = destSize};
+ }
}
bool hasGainmap() { return mGainmapBRD != nullptr; }
@@ -327,16 +368,16 @@
sp<uirenderer::Gainmap> gainmap;
bool hasGainmap = brd->hasGainmap();
if (hasGainmap) {
- int gainmapWidth = bitmap.width();
- int gainmapHeight = bitmap.height();
+ SkISize gainmapDims = SkISize::Make(bitmap.width(), bitmap.height());
if (javaBitmap) {
// If we are recycling we must match the inBitmap's relative dimensions
- gainmapWidth = recycledBitmap->width();
- gainmapHeight = recycledBitmap->height();
+ gainmapDims.fWidth = recycledBitmap->width();
+ gainmapDims.fHeight = recycledBitmap->height();
}
- SkIRect gainmapSubset = brd->calculateGainmapRegion(subset, &gainmapWidth, &gainmapHeight);
- if (!brd->decodeGainmapRegion(&gainmap, gainmapWidth, gainmapHeight, gainmapSubset,
- sampleSize, requireUnpremul)) {
+ BitmapRegionDecoderWrapper::Projection gainmapProjection =
+ brd->calculateGainmapRegion(subset, gainmapDims);
+ if (!brd->decodeGainmapRegion(&gainmap, gainmapProjection.destSize,
+ gainmapProjection.srcRect, sampleSize, requireUnpremul)) {
// If there is an error decoding Gainmap - we don't fail. We just don't provide Gainmap
hasGainmap = false;
}
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index a88139d..258bf91 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -1,12 +1,14 @@
#include <assert.h>
+#include <cutils/ashmem.h>
+#include <hwui/Canvas.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
#include <unistd.h>
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
#include "GraphicsJNI.h"
-
#include "SkBitmap.h"
#include "SkCanvas.h"
+#include "SkColor.h"
#include "SkColorSpace.h"
#include "SkFontMetrics.h"
#include "SkImageInfo.h"
@@ -14,10 +16,9 @@
#include "SkPoint.h"
#include "SkRect.h"
#include "SkRegion.h"
+#include "SkSamplingOptions.h"
#include "SkTypes.h"
-#include <cutils/ashmem.h>
-#include <hwui/Canvas.h>
-#include <log/log.h>
+#include "jni.h"
using namespace android;
@@ -630,13 +631,15 @@
////////////////////////////////////////////////////////////////////////////////
-RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap,
- bool mustMatchColorType)
+RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator(
+ android::Bitmap* recycledBitmap, bool mustMatchColorType,
+ std::optional<SkRect> desiredSubset)
: mRecycledBitmap(recycledBitmap)
, mRecycledBytes(recycledBitmap ? recycledBitmap->getAllocationByteCount() : 0)
, mSkiaBitmap(nullptr)
, mNeedsCopy(false)
- , mMustMatchColorType(mustMatchColorType) {}
+ , mMustMatchColorType(mustMatchColorType)
+ , mDesiredSubset(getSourceBoundsForUpsample(desiredSubset)) {}
RecyclingClippingPixelAllocator::~RecyclingClippingPixelAllocator() {}
@@ -668,7 +671,8 @@
const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight);
const size_t rowBytes = maxInfo.minRowBytes();
const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes);
- if (bytesNeeded <= mRecycledBytes) {
+
+ if (!mDesiredSubset && bytesNeeded <= mRecycledBytes) {
// Here we take advantage of reconfigure() to reset the rowBytes
// of mRecycledBitmap. It is very important that we pass in
// mRecycledBitmap->info() for the SkImageInfo. According to the
@@ -712,20 +716,31 @@
if (mNeedsCopy) {
mRecycledBitmap->ref();
android::Bitmap* recycledPixels = mRecycledBitmap;
- void* dst = recycledPixels->pixels();
- const size_t dstRowBytes = mRecycledBitmap->rowBytes();
- const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(),
- mSkiaBitmap->info().minRowBytes());
- const int rowsToCopy = std::min(mRecycledBitmap->info().height(),
- mSkiaBitmap->info().height());
- for (int y = 0; y < rowsToCopy; y++) {
- memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy);
- // Cast to bytes in order to apply the dstRowBytes offset correctly.
- dst = reinterpret_cast<void*>(
- reinterpret_cast<uint8_t*>(dst) + dstRowBytes);
+ if (mDesiredSubset) {
+ recycledPixels->setAlphaType(mSkiaBitmap->alphaType());
+ recycledPixels->setColorSpace(mSkiaBitmap->refColorSpace());
+
+ auto canvas = SkCanvas(recycledPixels->getSkBitmap());
+ SkRect destination = SkRect::Make(recycledPixels->info().bounds());
+ destination.intersect(SkRect::Make(mSkiaBitmap->info().bounds()));
+ canvas.drawImageRect(mSkiaBitmap->asImage(), *mDesiredSubset, destination,
+ SkSamplingOptions(SkFilterMode::kLinear), nullptr,
+ SkCanvas::kFast_SrcRectConstraint);
+ } else {
+ void* dst = recycledPixels->pixels();
+ const size_t dstRowBytes = mRecycledBitmap->rowBytes();
+ const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(),
+ mSkiaBitmap->info().minRowBytes());
+ const int rowsToCopy =
+ std::min(mRecycledBitmap->info().height(), mSkiaBitmap->info().height());
+ for (int y = 0; y < rowsToCopy; y++) {
+ memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy);
+ // Cast to bytes in order to apply the dstRowBytes offset correctly.
+ dst = reinterpret_cast<void*>(reinterpret_cast<uint8_t*>(dst) + dstRowBytes);
+ }
+ recycledPixels->setAlphaType(mSkiaBitmap->alphaType());
+ recycledPixels->setColorSpace(mSkiaBitmap->refColorSpace());
}
- recycledPixels->setAlphaType(mSkiaBitmap->alphaType());
- recycledPixels->setColorSpace(mSkiaBitmap->refColorSpace());
recycledPixels->notifyPixelsChanged();
recycledPixels->unref();
}
@@ -733,6 +748,20 @@
mSkiaBitmap = nullptr;
}
+std::optional<SkRect> RecyclingClippingPixelAllocator::getSourceBoundsForUpsample(
+ std::optional<SkRect> subset) {
+ if (!uirenderer::Properties::resampleGainmapRegions || !subset || subset->isEmpty()) {
+ return std::nullopt;
+ }
+
+ if (subset->left() == floor(subset->left()) && subset->top() == floor(subset->top()) &&
+ subset->right() == floor(subset->right()) && subset->bottom() == floor(subset->bottom())) {
+ return std::nullopt;
+ }
+
+ return subset;
+}
+
////////////////////////////////////////////////////////////////////////////////
AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) {
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index b0a1074..4b08f8d 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -216,8 +216,8 @@
*/
class RecyclingClippingPixelAllocator : public android::skia::BRDAllocator {
public:
- RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap,
- bool mustMatchColorType = true);
+ RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap, bool mustMatchColorType = true,
+ std::optional<SkRect> desiredSubset = std::nullopt);
~RecyclingClippingPixelAllocator();
@@ -241,11 +241,24 @@
SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; }
private:
+ /**
+ * Optionally returns a subset rectangle that we need to upsample from.
+ * E.g., a gainmap subset may be decoded in a slightly larger rectangle
+ * than is needed (in order to correctly preserve gainmap alignment when
+ * rendering at display time), so we need to re-sample the "intended"
+ * gainmap back up to the bitmap dimensions.
+ *
+ * If we don't need to upsample from a subregion, then returns an empty
+ * optional
+ */
+ static std::optional<SkRect> getSourceBoundsForUpsample(std::optional<SkRect> subset);
+
android::Bitmap* mRecycledBitmap;
const size_t mRecycledBytes;
SkBitmap* mSkiaBitmap;
bool mNeedsCopy;
const bool mMustMatchColorType;
+ const std::optional<SkRect> mDesiredSubset;
};
class AshmemPixelAllocator : public SkBitmap::Allocator {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
index e2ab316..c61a2ac 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
@@ -16,7 +16,9 @@
package com.android.packageinstaller.v2.ui
-import android.app.Activity
+import android.app.Activity.RESULT_CANCELED
+import android.app.Activity.RESULT_FIRST_USER
+import android.app.Activity.RESULT_OK
import android.app.AppOpsManager
import android.content.ActivityNotFoundException
import android.content.Intent
@@ -135,7 +137,7 @@
}
InstallAborted.ABORT_REASON_POLICY -> showPolicyRestrictionDialog(aborted)
- else -> setResult(Activity.RESULT_CANCELED, null, true)
+ else -> setResult(RESULT_CANCELED, null, true)
}
}
@@ -169,7 +171,7 @@
val success = installStage as InstallSuccess
if (success.shouldReturnResult) {
val successIntent = success.resultIntent
- setResult(Activity.RESULT_OK, successIntent, true)
+ setResult(RESULT_OK, successIntent, true)
} else {
val successDialog = InstallSuccessFragment(success)
showDialogInner(successDialog)
@@ -180,7 +182,7 @@
val failed = installStage as InstallFailed
if (failed.shouldReturnResult) {
val failureIntent = failed.resultIntent
- setResult(Activity.RESULT_FIRST_USER, failureIntent, true)
+ setResult(RESULT_FIRST_USER, failureIntent, true)
} else {
val failureDialog = InstallFailedFragment(failed)
showDialogInner(failureDialog)
@@ -219,7 +221,7 @@
shouldFinish = blockedByPolicyDialog == null
showDialogInner(blockedByPolicyDialog)
}
- setResult(Activity.RESULT_CANCELED, null, shouldFinish)
+ setResult(RESULT_CANCELED, null, shouldFinish)
}
/**
@@ -257,6 +259,10 @@
fun setResult(resultCode: Int, data: Intent?, shouldFinish: Boolean) {
super.setResult(resultCode, data)
+ if (resultCode != RESULT_OK) {
+ // Let callers know that the install was cancelled
+ installViewModel!!.cleanupInstall()
+ }
if (shouldFinish) {
finish()
}
@@ -282,7 +288,7 @@
if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
installViewModel!!.cleanupInstall()
}
- setResult(Activity.RESULT_CANCELED, null, true)
+ setResult(RESULT_CANCELED, null, true)
}
override fun onNegativeResponse(resultCode: Int, data: Intent?) {
@@ -318,7 +324,7 @@
if (localLogv) {
Log.d(LOG_TAG, "Opening $intent")
}
- setResult(Activity.RESULT_OK, intent, true)
+ setResult(RESULT_OK, intent, true)
if (intent != null && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
startActivity(intent)
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 7124ed2..c6eb9fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -178,7 +178,7 @@
}
log("updateRelationshipOfGroupDevices: mCachedDevices list =" + mCachedDevices.toString());
- // Get the preferred main device by getPreferredMainDeviceWithoutConectionState
+ // Get the preferred main device by getPreferredMainDeviceWithoutConnectionState
List<CachedBluetoothDevice> groupDevicesList = getGroupDevicesFromAllOfDevicesList(groupId);
CachedBluetoothDevice preferredMainDevice =
getPreferredMainDevice(groupId, groupDevicesList);
@@ -373,6 +373,7 @@
preferredMainDevice.addMemberDevice(deviceItem);
mCachedDevices.remove(deviceItem);
mBtManager.getEventManager().dispatchDeviceRemoved(deviceItem);
+ preferredMainDevice.refresh();
hasChanged = true;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 27fcdbe..26905b1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -80,6 +80,7 @@
"com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STATE_CHANGE";
public static final String EXTRA_LE_AUDIO_SHARING_STATE = "BLUETOOTH_LE_AUDIO_SHARING_STATE";
public static final String EXTRA_BLUETOOTH_DEVICE = "BLUETOOTH_DEVICE";
+ public static final String EXTRA_START_LE_AUDIO_SHARING = "START_LE_AUDIO_SHARING";
public static final int BROADCAST_STATE_UNKNOWN = 0;
public static final int BROADCAST_STATE_ON = 1;
public static final int BROADCAST_STATE_OFF = 2;
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
index 492828d..64e503b32 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
@@ -174,7 +174,6 @@
mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_OFF, null, TAG,
/* fromUser= */ true);
} else {
- // TODO: b/333527800 - This should (potentially) snooze the rule if it was active.
mNotificationManager.setAutomaticZenRuleState(mode.getId(),
new Condition(mode.getRule().getConditionId(), "", Condition.STATE_FALSE,
Condition.SOURCE_USER_ACTION));
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
index 69c7410..6198d80 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
@@ -181,14 +181,14 @@
* admin status.
*/
public Dialog createDialog(Activity activity,
- ActivityStarter activityStarter, boolean isMultipleAdminEnabled,
+ ActivityStarter activityStarter, boolean canCreateAdminUser,
NewUserData successCallback, Runnable cancelCallback) {
mActivity = activity;
mCustomDialogHelper = new CustomDialogHelper(activity);
mSuccessCallback = successCallback;
mCancelCallback = cancelCallback;
mActivityStarter = activityStarter;
- addCustomViews(isMultipleAdminEnabled);
+ addCustomViews(canCreateAdminUser);
mUserCreationDialog = mCustomDialogHelper.getDialog();
updateLayout();
mUserCreationDialog.setOnDismissListener(view -> finish());
@@ -197,19 +197,19 @@
return mUserCreationDialog;
}
- private void addCustomViews(boolean isMultipleAdminEnabled) {
+ private void addCustomViews(boolean canCreateAdminUser) {
addGrantAdminView();
addUserInfoEditView();
mCustomDialogHelper.setPositiveButton(R.string.next, view -> {
mCurrentState++;
- if (mCurrentState == GRANT_ADMIN_DIALOG && !isMultipleAdminEnabled) {
+ if (mCurrentState == GRANT_ADMIN_DIALOG && !canCreateAdminUser) {
mCurrentState++;
}
updateLayout();
});
mCustomDialogHelper.setNegativeButton(R.string.back, view -> {
mCurrentState--;
- if (mCurrentState == GRANT_ADMIN_DIALOG && !isMultipleAdminEnabled) {
+ if (mCurrentState == GRANT_ADMIN_DIALOG && !canCreateAdminUser) {
mCurrentState--;
}
updateLayout();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
index f94f21f..698eb81 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
@@ -19,7 +19,9 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
@@ -352,4 +354,34 @@
assertThat(mCachedDevice1.getMemberDevice()).contains(mCachedDevice3);
assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice);
}
+
+ @Test
+ public void onProfileConnectionStateChangedIfProcessed_addMemberDevice_refreshUI() {
+ mCachedDevice3.setGroupId(GROUP1);
+
+ mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+ BluetoothProfile.STATE_CONNECTED);
+
+ verify(mCachedDevice1).refresh();
+ }
+
+ @Test
+ public void onProfileConnectionStateChangedIfProcessed_switchMainDevice_refreshUI() {
+ when(mDevice3.isConnected()).thenReturn(true);
+ when(mDevice2.isConnected()).thenReturn(false);
+ when(mDevice1.isConnected()).thenReturn(false);
+ mCachedDevice3.setGroupId(GROUP1);
+ mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+ BluetoothProfile.STATE_CONNECTED);
+
+ when(mDevice3.isConnected()).thenReturn(false);
+ mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+ BluetoothProfile.STATE_DISCONNECTED);
+ when(mDevice1.isConnected()).thenReturn(true);
+ mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
+ BluetoothProfile.STATE_CONNECTED);
+
+ verify(mCachedDevice3).switchMemberDeviceContent(mCachedDevice1);
+ verify(mCachedDevice3, atLeastOnce()).refresh();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
index 00c7ae3..539519b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
@@ -123,7 +123,6 @@
zenRule.id = id;
zenRule.pkg = "package";
zenRule.enabled = azr.isEnabled();
- zenRule.snoozing = false;
zenRule.conditionId = azr.getConditionId();
zenRule.condition = new Condition(azr.getConditionId(), "",
active ? Condition.STATE_TRUE : Condition.STATE_FALSE,
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index e6fae7b..9046d4e 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -156,17 +156,6 @@
}
flag {
- name: "pss_app_selector_abrupt_exit_fix"
- namespace: "systemui"
- description: "Fixes the app selector abruptly disappearing without an animation, when the"
- "selected task is the foreground task."
- bug: "314385883"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "pss_app_selector_recents_split_screen"
namespace: "systemui"
description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.kt
new file mode 100644
index 0000000..8f5cdbf
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.getParentOfType
+
+/**
+ * Checks if registerContentObserver/registerContentObserverAsUser/unregisterContentObserver is
+ * called on a ContentResolver (or subclasses), and directs the caller to using
+ * com.android.systemui.util.settings.SettingsProxy or its sub-classes.
+ */
+@Suppress("UnstableApiUsage")
+class RegisterContentObserverViaContentResolverDetector : Detector(), SourceCodeScanner {
+
+ override fun getApplicableMethodNames(): List<String> {
+ return CONTENT_RESOLVER_METHOD_LIST
+ }
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ val classQualifiedName = node.getParentOfType(UClass::class.java)?.qualifiedName
+ if (classQualifiedName in CLASSNAME_ALLOWLIST) {
+ // Don't warn for class we want the developers to use.
+ return
+ }
+
+ val evaluator = context.evaluator
+ if (evaluator.isMemberInSubClassOf(method, "android.content.ContentResolver")) {
+ context.report(
+ issue = CONTENT_RESOLVER_ERROR,
+ location = context.getNameLocation(node),
+ message =
+ "`ContentResolver.${method.name}()` should be replaced with " +
+ "an appropriate interface API call, for eg. " +
+ "`<SettingsProxy>/<UserSettingsProxy>.${method.name}()`"
+ )
+ }
+ }
+
+ companion object {
+ @JvmField
+ val CONTENT_RESOLVER_ERROR: Issue =
+ Issue.create(
+ id = "RegisterContentObserverViaContentResolver",
+ briefDescription =
+ "Content observer registration done via `ContentResolver`" +
+ "instead of `SettingsProxy or child interfaces.`",
+ // lint trims indents and converts \ to line continuations
+ explanation =
+ """
+ Use registerContentObserver/unregisterContentObserver methods in \
+ `SettingsProxy`, `UserSettingsProxy` or `GlobalSettings` class instead of \
+ using `ContentResolver.registerContentObserver` or \
+ `ContentResolver.unregisterContentObserver`.""",
+ category = Category.PERFORMANCE,
+ priority = 10,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ RegisterContentObserverViaContentResolverDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private val CLASSNAME_ALLOWLIST =
+ listOf(
+ "com.android.systemui.util.settings.SettingsProxy",
+ "com.android.systemui.util.settings.UserSettingsProxy",
+ "com.android.systemui.util.settings.GlobalSettings",
+ "com.android.systemui.util.settings.SecureSettings",
+ "com.android.systemui.util.settings.SystemSettings"
+ )
+
+ private val CONTENT_RESOLVER_METHOD_LIST =
+ listOf(
+ "registerContentObserver",
+ "registerContentObserverAsUser",
+ "unregisterContentObserver"
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 5206b05..a1f4f55 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -46,7 +46,8 @@
DemotingTestWithoutBugDetector.ISSUE,
TestFunctionNameViolationDetector.ISSUE,
MissingApacheLicenseDetector.ISSUE,
- RegisterContentObserverSyncViaSettingsProxyDetector.SYNC_WARNING
+ RegisterContentObserverSyncViaSettingsProxyDetector.SYNC_WARNING,
+ RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR
)
override val api: Int
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt
new file mode 100644
index 0000000..1d33bce
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2024 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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class RegisterContentObserverViaContentResolverDetectorTest : SystemUILintDetectorTest() {
+
+ override fun getDetector(): Detector = RegisterContentObserverViaContentResolverDetector()
+
+ override fun getIssues(): List<Issue> =
+ listOf(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+
+ @Test
+ fun testRegisterContentObserver_throwError() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass {
+ public void register(Context context) {
+ context.getContentResolver().
+ registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass.java:7: Error: ContentResolver.registerContentObserver() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.registerContentObserver() [RegisterContentObserverViaContentResolver]
+ registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ )
+ }
+
+ @Test
+ fun testRegisterContentObserverForUser_throwError() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass {
+ public void register(Context context) {
+ context.getContentResolver().
+ registerContentObserverAsUser(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass.java:7: Error: ContentResolver.registerContentObserverAsUser() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.registerContentObserverAsUser() [RegisterContentObserverViaContentResolver]
+ registerContentObserverAsUser(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+ .trimIndent()
+ )
+ }
+
+ @Test
+ fun testSuppressRegisterContentObserver() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass {
+ @SuppressWarnings("RegisterContentObserverViaContentResolver")
+ public void register(Context context) {
+ context.getContentResolver().
+ registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testRegisterContentObserverInSettingsProxy_allowed() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package com.android.systemui.util.settings;
+ import android.content.Context;
+
+ public class SettingsProxy {
+ public void register(Context context) {
+ context.getContentResolver().
+ registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testNoopIfNoCall() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class SettingsProxy {
+ public void register(Context context) {
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testUnRegisterContentObserver_throwError() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass {
+ public void register(Context context) {
+ context.getContentResolver().
+ unregisterContentObserver(mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass.java:7: Error: ContentResolver.unregisterContentObserver() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver() [RegisterContentObserverViaContentResolver]
+ unregisterContentObserver(mSettingObserver);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
new file mode 100644
index 0000000..04bcc36
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2024 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.composable
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.modifiers.background
+import com.android.compose.modifiers.height
+import com.android.compose.modifiers.width
+import com.android.systemui.deviceentry.shared.model.BiometricMessage
+import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
+import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
+import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.binder.AlternateBouncerUdfpsViewBinder
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
+import com.android.systemui.res.R
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+@Composable
+fun AlternateBouncer(
+ alternateBouncerDependencies: AlternateBouncerDependencies,
+ modifier: Modifier = Modifier,
+) {
+
+ val isVisible by
+ alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle(
+ initialValue = false
+ )
+
+ val udfpsIconLocation by
+ alternateBouncerDependencies.udfpsIconViewModel.iconLocation.collectAsStateWithLifecycle(
+ initialValue = null
+ )
+
+ // TODO (b/353955910): back handling doesn't work
+ BackHandler { alternateBouncerDependencies.viewModel.onBackRequested() }
+
+ AnimatedVisibility(
+ visible = isVisible,
+ enter = fadeIn(),
+ exit = fadeOut(),
+ modifier = modifier,
+ ) {
+ Box(
+ contentAlignment = Alignment.TopCenter,
+ modifier =
+ Modifier.background(color = Colors.AlternateBouncerBackgroundColor, alpha = { 1f })
+ .pointerInput(Unit) {
+ detectTapGestures(
+ onTap = { alternateBouncerDependencies.viewModel.onTapped() }
+ )
+ },
+ ) {
+ StatusMessage(
+ viewModel = alternateBouncerDependencies.messageAreaViewModel,
+ )
+ }
+
+ udfpsIconLocation?.let { udfpsLocation ->
+ Box {
+ DeviceEntryIcon(
+ viewModel = alternateBouncerDependencies.udfpsIconViewModel,
+ modifier =
+ Modifier.width { udfpsLocation.width }
+ .height { udfpsLocation.height }
+ .fillMaxHeight()
+ .offset {
+ IntOffset(
+ x = udfpsLocation.left,
+ y = udfpsLocation.top,
+ )
+ },
+ )
+ }
+
+ UdfpsA11yOverlay(
+ viewModel = alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel.get(),
+ modifier = Modifier.fillMaxHeight(),
+ )
+ }
+ }
+}
+
+@ExperimentalCoroutinesApi
+@Composable
+private fun StatusMessage(
+ viewModel: AlternateBouncerMessageAreaViewModel,
+ modifier: Modifier = Modifier,
+) {
+ val message: BiometricMessage? by
+ viewModel.message.collectAsStateWithLifecycle(initialValue = null)
+
+ Crossfade(
+ targetState = message,
+ label = "Alternate Bouncer message",
+ animationSpec = tween(),
+ modifier = modifier,
+ ) { biometricMessage ->
+ biometricMessage?.let {
+ Text(
+ textAlign = TextAlign.Center,
+ text = it.message ?: "",
+ color = Colors.AlternateBouncerTextColor,
+ fontSize = 18.sp,
+ lineHeight = 24.sp,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.padding(top = 92.dp),
+ )
+ }
+ }
+}
+
+@ExperimentalCoroutinesApi
+@Composable
+private fun DeviceEntryIcon(
+ viewModel: AlternateBouncerUdfpsIconViewModel,
+ modifier: Modifier = Modifier,
+) {
+ AndroidView(
+ modifier = modifier,
+ factory = { context ->
+ val view =
+ DeviceEntryIconView(context, null).apply {
+ id = R.id.alternate_bouncer_udfps_icon_view
+ contentDescription =
+ context.resources.getString(R.string.accessibility_fingerprint_label)
+ }
+ AlternateBouncerUdfpsViewBinder.bind(view, viewModel)
+ view
+ },
+ )
+}
+
+/** TODO (b/353955910): Validate accessibility CUJs */
+@ExperimentalCoroutinesApi
+@Composable
+private fun UdfpsA11yOverlay(
+ viewModel: AlternateBouncerUdfpsAccessibilityOverlayViewModel,
+ modifier: Modifier = Modifier,
+) {
+ AndroidView(
+ factory = { context ->
+ val view =
+ UdfpsAccessibilityOverlay(context).apply {
+ id = R.id.alternate_bouncer_udfps_accessibility_overlay
+ }
+ UdfpsAccessibilityOverlayBinder.bind(view, viewModel)
+ view
+ },
+ modifier = modifier,
+ )
+}
+
+private object Colors {
+ val AlternateBouncerBackgroundColor: Color = Color.Black.copy(alpha = .66f)
+ val AlternateBouncerTextColor: Color = Color.White
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
index 9afb4d5..a78c038 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.composable
import com.android.systemui.keyguard.ui.composable.blueprint.CommunalBlueprintModule
-import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
import com.android.systemui.keyguard.ui.composable.section.OptionalSectionModule
import dagger.Module
@@ -26,7 +25,6 @@
[
CommunalBlueprintModule::class,
OptionalSectionModule::class,
- ShortcutsBesideUdfpsBlueprintModule::class,
],
)
interface LockscreenSceneBlueprintModule
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
deleted file mode 100644
index a5e120c..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * 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.composable.blueprint
-
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.unit.IntRect
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.padding
-import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
-import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
-import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
-import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
-import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
-import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.IntoSet
-import java.util.Optional
-import javax.inject.Inject
-import kotlin.math.roundToInt
-
-/**
- * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form
- * factor).
- */
-class ShortcutsBesideUdfpsBlueprint
-@Inject
-constructor(
- private val statusBarSection: StatusBarSection,
- private val lockSection: LockSection,
- private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
- private val bottomAreaSection: BottomAreaSection,
- private val settingsMenuSection: SettingsMenuSection,
- private val topAreaSection: TopAreaSection,
- private val notificationSection: NotificationSection,
-) : ComposableLockscreenSceneBlueprint {
-
- override val id: String = "shortcuts-besides-udfps"
-
- @Composable
- override fun SceneScope.Content(
- viewModel: LockscreenContentViewModel,
- modifier: Modifier,
- ) {
- val isUdfpsVisible = viewModel.isUdfpsVisible
- val isShadeLayoutWide by viewModel.isShadeLayoutWide.collectAsStateWithLifecycle()
- val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
- val areNotificationsVisible by
- viewModel
- .areNotificationsVisible(contentKey)
- .collectAsStateWithLifecycle(initialValue = false)
-
- LockscreenLongPress(
- viewModel = viewModel.touchHandling,
- modifier = modifier,
- ) { onSettingsMenuPlaced ->
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxSize(),
- ) {
- with(statusBarSection) {
- StatusBar(
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- horizontal = { unfoldTranslations.start.roundToInt() },
- )
- )
- }
-
- Box {
- with(topAreaSection) {
- DefaultClockLayout(
- smartSpacePaddingTop = viewModel::getSmartSpacePaddingTop,
- modifier =
- Modifier.graphicsLayer {
- translationX = unfoldTranslations.start
- },
- )
- }
- if (isShadeLayoutWide) {
- with(notificationSection) {
- Notifications(
- areNotificationsVisible = areNotificationsVisible,
- isShadeLayoutWide = isShadeLayoutWide,
- burnInParams = null,
- modifier =
- Modifier.fillMaxWidth(0.5f)
- .fillMaxHeight()
- .align(alignment = Alignment.TopEnd)
- )
- }
- }
- }
- if (!isShadeLayoutWide) {
- with(notificationSection) {
- Notifications(
- areNotificationsVisible = areNotificationsVisible,
- isShadeLayoutWide = isShadeLayoutWide,
- burnInParams = null,
- modifier = Modifier.weight(weight = 1f)
- )
- }
- }
- if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- // Constrained to the left of the lock icon (in left-to-right layouts).
- with(bottomAreaSection) {
- Shortcut(
- isStart = true,
- applyPadding = false,
- modifier =
- Modifier.graphicsLayer { translationX = unfoldTranslations.start },
- )
- }
-
- with(lockSection) { LockIcon() }
-
- // Constrained to the right of the lock icon (in left-to-right layouts).
- with(bottomAreaSection) {
- Shortcut(
- isStart = false,
- applyPadding = false,
- modifier =
- Modifier.graphicsLayer { translationX = unfoldTranslations.end },
- )
- }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(modifier = Modifier.fillMaxWidth())
- }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
- },
- modifier = Modifier.fillMaxSize(),
- ) { measurables, constraints ->
- check(measurables.size == 6)
- val aboveLockIconMeasurable = measurables[0]
- val startSideShortcutMeasurable = measurables[1]
- val lockIconMeasurable = measurables[2]
- val endSideShortcutMeasurable = measurables[3]
- val belowLockIconMeasurable = measurables[4]
- val settingsMenuMeasurable = measurables[5]
-
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
-
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
-
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val startSideShortcutPlaceable =
- startSideShortcutMeasurable.measure(noMinConstraints)
- val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(
- maxHeight = constraints.maxHeight - lockIconBounds.bottom
- )
- )
- val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- startSideShortcutPlaceable.placeRelative(
- x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
- y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- endSideShortcutPlaceable.placeRelative(
- x =
- lockIconBounds.right +
- (constraints.maxWidth - lockIconBounds.right) / 2 -
- endSideShortcutPlaceable.width / 2,
- y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- settingsMenuPlaceable.place(
- x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
- y = constraints.maxHeight - settingsMenuPlaceable.height,
- )
- }
- }
- }
- }
-}
-
-@Module
-interface ShortcutsBesideUdfpsBlueprintModule {
- @Binds
- @IntoSet
- fun blueprint(blueprint: ShortcutsBesideUdfpsBlueprint): ComposableLockscreenSceneBlueprint
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 9c72d93..364adca 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -32,7 +32,6 @@
import androidx.core.content.res.ResourcesCompat
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
@@ -40,10 +39,8 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
-import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
-import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
@@ -52,11 +49,9 @@
@Inject
constructor(
private val viewModel: KeyguardQuickAffordancesCombinedViewModel,
- private val falsingManager: FalsingManager,
- private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
- private val shortcutsLogger: KeyguardQuickAffordancesLogger,
+ private val keyguardQuickAffordanceViewBinder: KeyguardQuickAffordanceViewBinder,
) {
/**
* Renders a single lockscreen shortcut.
@@ -80,9 +75,8 @@
viewId = if (isStart) R.id.start_button else R.id.end_button,
viewModel = if (isStart) viewModel.startButton else viewModel.endButton,
transitionAlpha = viewModel.transitionAlpha,
- falsingManager = falsingManager,
- vibratorHelper = vibratorHelper,
indicationController = indicationController,
+ binder = keyguardQuickAffordanceViewBinder,
modifier =
if (applyPadding) {
Modifier.shortcutPadding()
@@ -124,9 +118,8 @@
@IdRes viewId: Int,
viewModel: Flow<KeyguardQuickAffordanceViewModel>,
transitionAlpha: Flow<Float>,
- falsingManager: FalsingManager,
- vibratorHelper: VibratorHelper,
indicationController: KeyguardIndicationController,
+ binder: KeyguardQuickAffordanceViewBinder,
modifier: Modifier = Modifier,
) {
val (binding, setBinding) = mutableStateOf<KeyguardQuickAffordanceViewBinder.Binding?>(null)
@@ -158,13 +151,10 @@
}
setBinding(
- KeyguardQuickAffordanceViewBinder.bind(
+ binder.bind(
view,
viewModel,
transitionAlpha,
- falsingManager,
- vibratorHelper,
- shortcutsLogger,
) {
indicationController.showTransientIndication(it)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 9da2a1b..5ffb6f8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -50,7 +50,7 @@
) {
val accessibilityTitle = stringResource(R.string.accessibility_volume_settings)
val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle()
- val components by viewModel.componentsLayout.collectAsStateWithLifecycle(null)
+ val components by viewModel.componentsLayout.collectAsStateWithLifecycle()
with(VolumePanelComposeScope(state)) {
components?.let { componentsState ->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
new file mode 100644
index 0000000..5eabd22
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 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.compose.animation.scene
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.SpringSpec
+import com.android.compose.animation.scene.content.state.ContentState
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+internal fun CoroutineScope.animateContent(
+ transition: ContentState.Transition<*>,
+ oneOffAnimation: OneOffAnimation,
+ targetProgress: Float,
+ startTransition: () -> Unit,
+ finishTransition: () -> Unit,
+) {
+ // Start the transition. This will compute the TransformationSpec associated to [transition],
+ // which we need to initialize the Animatable that will actually animate it.
+ startTransition()
+
+ // The transition now contains the transformation spec that we should use to instantiate the
+ // Animatable.
+ val animationSpec = transition.transformationSpec.progressSpec
+ val visibilityThreshold =
+ (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
+ val replacedTransition = transition.replacedTransition
+ val initialProgress = replacedTransition?.progress ?: 0f
+ val initialVelocity = replacedTransition?.progressVelocity ?: 0f
+ val animatable =
+ Animatable(initialProgress, visibilityThreshold = visibilityThreshold).also {
+ oneOffAnimation.animatable = it
+ }
+
+ // Animate the progress to its target value.
+ //
+ // Important: We start atomically to make sure that we start the coroutine even if it is
+ // cancelled right after it is launched, so that finishTransition() is correctly called.
+ // Otherwise, this transition will never be stopped and we will never settle to Idle.
+ oneOffAnimation.job =
+ launch(start = CoroutineStart.ATOMIC) {
+ try {
+ animatable.animateTo(targetProgress, animationSpec, initialVelocity)
+ } finally {
+ finishTransition()
+ }
+ }
+}
+
+internal class OneOffAnimation {
+ /**
+ * The animatable used to animate this transition.
+ *
+ * Note: This is lateinit because we need to first create this object so that
+ * [SceneTransitionLayoutState] can compute the transformations and animation spec associated to
+ * the transition, which is needed to initialize this Animatable.
+ */
+ lateinit var animatable: Animatable<Float, AnimationVector1D>
+
+ /** The job that is animating [animatable]. */
+ lateinit var job: Job
+
+ val progress: Float
+ get() = animatable.value
+
+ val progressVelocity: Float
+ get() = animatable.velocity
+
+ fun finish(): Job = job
+}
+
+// TODO(b/290184746): Compute a good default visibility threshold that depends on the layout size
+// and screen density.
+internal const val ProgressVisibilityThreshold = 1e-3f
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 1fc1f98..68a6c98 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -16,15 +16,10 @@
package com.android.compose.animation.scene
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.SpringSpec
import com.android.compose.animation.scene.content.state.TransitionState
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
/**
* Transition to [target] using a canned animation. This function will try to be smart and take over
@@ -50,7 +45,7 @@
return when (transitionState) {
is TransitionState.Idle -> {
- animate(
+ animateToScene(
layoutState,
target,
transitionKey,
@@ -80,13 +75,11 @@
} else {
// The transition is in progress: start the canned animation at the same
// progress as it was in.
- animate(
+ animateToScene(
layoutState,
target,
transitionKey,
isInitiatedByUserInput,
- initialProgress = progress,
- initialVelocity = transitionState.progressVelocity,
replacedTransition = transitionState,
)
}
@@ -102,13 +95,11 @@
layoutState.finishTransition(transitionState, target)
null
} else {
- animate(
+ animateToScene(
layoutState,
target,
transitionKey,
isInitiatedByUserInput,
- initialProgress = progress,
- initialVelocity = transitionState.progressVelocity,
reversed = true,
replacedTransition = transitionState,
)
@@ -140,7 +131,7 @@
animateToScene(layoutState, animateFrom, transitionKey = null)
}
- animate(
+ animateToScene(
layoutState,
target,
transitionKey,
@@ -154,103 +145,68 @@
}
}
-private fun CoroutineScope.animate(
+private fun CoroutineScope.animateToScene(
layoutState: MutableSceneTransitionLayoutStateImpl,
targetScene: SceneKey,
transitionKey: TransitionKey?,
isInitiatedByUserInput: Boolean,
replacedTransition: TransitionState.Transition?,
- initialProgress: Float = 0f,
- initialVelocity: Float = 0f,
reversed: Boolean = false,
fromScene: SceneKey = layoutState.transitionState.currentScene,
chain: Boolean = true,
): TransitionState.Transition {
+ val oneOffAnimation = OneOffAnimation()
val targetProgress = if (reversed) 0f else 1f
val transition =
if (reversed) {
- OneOffTransition(
+ OneOffSceneTransition(
key = transitionKey,
fromScene = targetScene,
toScene = fromScene,
currentScene = targetScene,
isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = false,
replacedTransition = replacedTransition,
+ oneOffAnimation = oneOffAnimation,
)
} else {
- OneOffTransition(
+ OneOffSceneTransition(
key = transitionKey,
fromScene = fromScene,
toScene = targetScene,
currentScene = targetScene,
isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = false,
replacedTransition = replacedTransition,
+ oneOffAnimation = oneOffAnimation,
)
}
- // Change the current layout state to start this new transition. This will compute the
- // TransformationSpec associated to this transition, which we need to initialize the Animatable
- // that will actually animate it.
- layoutState.startTransition(transition, chain)
-
- // The transition now contains the transformation spec that we should use to instantiate the
- // Animatable.
- val animationSpec = transition.transformationSpec.progressSpec
- val visibilityThreshold =
- (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
- val animatable =
- Animatable(initialProgress, visibilityThreshold = visibilityThreshold).also {
- transition.animatable = it
- }
-
- // Animate the progress to its target value.
- // Important: We start atomically to make sure that we start the coroutine even if it is
- // cancelled right after it is launched, so that finishTransition() is correctly called.
- // Otherwise, this transition will never be stopped and we will never settle to Idle.
- transition.job =
- launch(start = CoroutineStart.ATOMIC) {
- try {
- animatable.animateTo(targetProgress, animationSpec, initialVelocity)
- } finally {
- layoutState.finishTransition(transition, targetScene)
- }
- }
+ animateContent(
+ transition = transition,
+ oneOffAnimation = oneOffAnimation,
+ targetProgress = targetProgress,
+ startTransition = { layoutState.startTransition(transition, chain) },
+ finishTransition = { layoutState.finishTransition(transition, targetScene) },
+ )
return transition
}
-private class OneOffTransition(
+private class OneOffSceneTransition(
override val key: TransitionKey?,
fromScene: SceneKey,
toScene: SceneKey,
override val currentScene: SceneKey,
override val isInitiatedByUserInput: Boolean,
- override val isUserInputOngoing: Boolean,
replacedTransition: TransitionState.Transition?,
+ private val oneOffAnimation: OneOffAnimation,
) : TransitionState.Transition(fromScene, toScene, replacedTransition) {
- /**
- * The animatable used to animate this transition.
- *
- * Note: This is lateinit because we need to first create this Transition object so that
- * [SceneTransitionLayoutState] can compute the transformations and animation spec associated to
- * it, which is need to initialize this Animatable.
- */
- lateinit var animatable: Animatable<Float, AnimationVector1D>
-
- /** The job that is animating [animatable]. */
- lateinit var job: Job
-
override val progress: Float
- get() = animatable.value
+ get() = oneOffAnimation.progress
override val progressVelocity: Float
- get() = animatable.velocity
+ get() = oneOffAnimation.progressVelocity
- override fun finish(): Job = job
+ override val isUserInputOngoing: Boolean = false
+
+ override fun finish(): Job = oneOffAnimation.finish()
}
-
-// TODO(b/290184746): Compute a good default visibility threshold that depends on the layout size
-// and screen density.
-internal const val ProgressVisibilityThreshold = 1e-3f
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index 0105af3..fe16ef751 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -127,16 +127,7 @@
return coroutineScope
.launch(start = CoroutineStart.ATOMIC) {
try {
- if (currentScene == toScene) {
- animatable.animateTo(targetProgress, transformationSpec.progressSpec)
- } else {
- // If the back gesture is cancelled, the progress is animated back to 0f by
- // the system. But we need this animate call anyways because
- // PredictiveBackHandler doesn't guarantee that it ends at 0f. Since the
- // remaining change in progress is usually very small, the progressSpec is
- // omitted and the default spring spec used instead.
- animatable.animateTo(targetProgress)
- }
+ animatable.animateTo(targetProgress)
} finally {
state.finishTransition(this@PredictiveBackTransition, scene)
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
index c414fbe..0eaecb0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
@@ -18,8 +18,6 @@
import androidx.activity.BackEventCompat
import androidx.activity.ComponentActivity
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.rememberCoroutineScope
@@ -61,23 +59,7 @@
@Test
fun testPredictiveBack() {
- val transitionFrames = 2
- val layoutState =
- rule.runOnUiThread {
- MutableSceneTransitionLayoutState(
- SceneA,
- transitions =
- transitions {
- from(SceneA, to = SceneB) {
- spec =
- tween(
- durationMillis = transitionFrames * 16,
- easing = LinearEasing
- )
- }
- }
- )
- }
+ val layoutState = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
rule.setContent {
SceneTransitionLayout(layoutState) {
scene(SceneA, mapOf(Back to SceneB)) { Box(Modifier.fillMaxSize()) }
@@ -106,27 +88,12 @@
assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
assertThat(layoutState.transitionState).isIdle()
- rule.mainClock.autoAdvance = false
-
// Start again and commit it.
rule.runOnUiThread {
dispatcher.dispatchOnBackStarted(backEvent())
dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
dispatcher.onBackPressed()
}
- rule.mainClock.advanceTimeByFrame()
- rule.waitForIdle()
- val transition2 = assertThat(layoutState.transitionState).isTransition()
- // verify that transition picks up progress from preview
- assertThat(transition2).hasProgress(0.4f, tolerance = 0.0001f)
-
- rule.mainClock.advanceTimeByFrame()
- rule.waitForIdle()
- // verify that transition is half way between preview-end-state (0.4f) and target-state (1f)
- // after one frame
- assertThat(transition2).hasProgress(0.7f, tolerance = 0.0001f)
-
- rule.mainClock.autoAdvance = true
rule.waitForIdle()
assertThat(layoutState.transitionState).hasCurrentScene(SceneB)
assertThat(layoutState.transitionState).isIdle()
diff --git a/packages/SystemUI/lint-baseline.xml b/packages/SystemUI/lint-baseline.xml
index b4c839f..7577147 100644
--- a/packages/SystemUI/lint-baseline.xml
+++ b/packages/SystemUI/lint-baseline.xml
@@ -32157,4 +32157,631 @@
column="6"/>
</issue>
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt"
+ line="154"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(ALWAYS_ON_DISPLAY_CONSTANTS_URI,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java"
+ line="164"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt"
+ line="61"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" awaitClose { resolver.unregisterContentObserver(observer) }"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt"
+ line="67"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mSettingObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+ line="123"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mSettingObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+ line="180"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+ line="211"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+ line="219"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+ line="46"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+ line="48"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" resolver.unregisterContentObserver(allowedObserver)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+ line="54"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" resolver.unregisterContentObserver(onObserver)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+ line="55"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(mQuickPickupGesture, false, this,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+ line="511"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(mPickupGesture, false, this, UserHandle.USER_ALL);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+ line="513"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(mAlwaysOnEnabled, false, this,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+ line="514"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="2470"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="3077"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="3168"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="3957"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="3961"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+ line="486"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+ line="492"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" contentResolver.unregisterContentObserver(settingsObserver)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+ line="543"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt"
+ line="82"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" contentResolver.unregisterContentObserver(settingsObserver)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt"
+ line="100"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mMenuTargetFeaturesContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+ line="275"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+ line="276"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+ line="277"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+ line="200"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver(Global.getUriFor("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+ line="202"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+ line="212"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" context.contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt"
+ line="54"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="253"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="256"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="259"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="262"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mAssistContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="295"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java"
+ line="395"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java"
+ line="400"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java"
+ line="3675"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mSettingsChangeObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java"
+ line="4705"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(Settings.Global.getUriFor("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+ line="191"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+ line="205"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+ line="216"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java"
+ line="178"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java"
+ line="186"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java"
+ line="83"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java"
+ line="100"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+ line="219"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+ line="241"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+ line="243"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+ line="1235"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+ line="1236"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+ line="1240"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mResolver.unregisterContentObserver(this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+ line="377"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+ line="379"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+ line="381"
+ column="23"/>
+ </issue>
+
</issues>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
new file mode 100644
index 0000000..1f73347
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2024 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.education.domain.ui.view
+
+import android.content.applicationContext
+import android.widget.Toast
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
+import com.android.systemui.education.domain.interactor.contextualEducationInteractor
+import com.android.systemui.education.domain.interactor.keyboardTouchpadEduInteractor
+import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
+import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ContextualEduUiCoordinatorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val interactor = kosmos.contextualEducationInteractor
+ private lateinit var underTest: ContextualEduUiCoordinator
+ @Mock private lateinit var toast: Toast
+
+ @get:Rule val mockitoRule = MockitoJUnit.rule()
+
+ @Before
+ fun setUp() {
+ val viewModel =
+ ContextualEduViewModel(
+ kosmos.applicationContext.resources,
+ kosmos.keyboardTouchpadEduInteractor
+ )
+ underTest =
+ ContextualEduUiCoordinator(kosmos.applicationCoroutineScope, viewModel) { _ -> toast }
+ underTest.start()
+ kosmos.keyboardTouchpadEduInteractor.start()
+ }
+
+ @Test
+ @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+ fun showToastOnNewEdu() =
+ testScope.runTest {
+ triggerEducation(BACK)
+ runCurrent()
+ verify(toast).show()
+ }
+
+ private suspend fun triggerEducation(gestureType: GestureType) {
+ for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+ interactor.incrementSignalCount(gestureType)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index dc225a3..638c957 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -16,8 +16,6 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
-import com.android.systemui.coroutines.collectLastValue
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -28,18 +26,16 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
import com.android.systemui.kosmos.testScope
-import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -49,7 +45,6 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
-import com.google.common.truth.Truth.assertThat
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 90e13a5..8c1e8de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -757,6 +757,30 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
+ fun goneToOccluded() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to GONE
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+
+ // WHEN an occluding app is running and showDismissibleKeyguard is called
+ keyguardRepository.setKeyguardOccluded(true)
+ keyguardRepository.showDismissibleKeyguard()
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.GONE,
+ to = KeyguardState.OCCLUDED,
+ ownerName =
+ "FromGoneTransitionInteractor" + "(Dismissible keyguard with occlusion)",
+ animatorAssertion = { it.isNotNull() }
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
@DisableSceneContainer
fun goneToDreaming() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt
index 3f6e229..df8afdb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModelTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -117,6 +118,24 @@
}
@Test
+ fun lockscreenAlphaStartsFromViewStateAccessorAlpha() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor(alpha = { 0.5f })
+ val alpha by collectLastValue(underTest.lockscreenAlpha(viewState))
+
+ keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
+
+ keyguardTransitionRepository.sendTransitionStep(step(0f))
+ assertThat(alpha).isEqualTo(0.5f)
+
+ keyguardTransitionRepository.sendTransitionStep(step(0.5f))
+ assertThat(alpha).isEqualTo(0.75f)
+
+ keyguardTransitionRepository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
fun deviceEntryBackgroundViewDisappear() =
testScope.runTest {
val values by collectValues(underTest.deviceEntryBackgroundViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
index d1f908d..46b370f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
@@ -16,16 +16,27 @@
package com.android.systemui.lifecycle
+import android.view.View
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.Assert
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -110,4 +121,45 @@
assertThat(isActive).isFalse()
}
+
+ @Test
+ fun viewModel_viewBinder() = runTest {
+ Assert.setTestThread(Thread.currentThread())
+
+ val view: View = mock { on { isAttachedToWindow } doReturn false }
+ val viewModel = FakeViewModel()
+ backgroundScope.launch {
+ view.viewModel(
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = { viewModel },
+ ) {
+ awaitCancellation()
+ }
+ }
+ runCurrent()
+
+ assertThat(viewModel.isActivated).isFalse()
+
+ view.stub { on { isAttachedToWindow } doReturn true }
+ argumentCaptor<View.OnAttachStateChangeListener>()
+ .apply { verify(view).addOnAttachStateChangeListener(capture()) }
+ .allValues
+ .forEach { it.onViewAttachedToWindow(view) }
+ runCurrent()
+
+ assertThat(viewModel.isActivated).isTrue()
+ }
+}
+
+private class FakeViewModel : SysUiViewModel() {
+ var isActivated = false
+
+ override suspend fun onActivated() {
+ isActivated = true
+ try {
+ awaitCancellation()
+ } finally {
+ isActivated = false
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index 8a43198..fadb1d7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -22,13 +22,13 @@
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.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.kosmos.testScope
-import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
@@ -43,6 +43,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -50,7 +51,7 @@
private val configurationRepository = kosmos.fakeConfigurationRepository
private val keyguardRepository = kosmos.fakeKeyguardRepository
private val sceneInteractor = kosmos.sceneInteractor
- private val shadeRepository = kosmos.shadeRepository
+ private val shadeTestUtil = kosmos.shadeTestUtil
private val underTest = kosmos.shadeInteractorSceneContainerImpl
@@ -60,7 +61,7 @@
val actual by collectLastValue(underTest.qsExpansion)
// WHEN split shade is enabled and QS is expanded
- overrideResource(R.bool.config_use_split_notification_shade, true)
+ shadeTestUtil.setSplitShade(true)
configurationRepository.onAnyConfigurationChange()
runCurrent()
val transitionState =
@@ -89,7 +90,7 @@
// WHEN split shade is not enabled and QS is expanded
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
configurationRepository.onAnyConfigurationChange()
runCurrent()
val progress = MutableStateFlow(.3f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 2fb9e1e0..733cac9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -56,6 +56,7 @@
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.mockLargeScreenHeaderHelper
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -135,11 +136,14 @@
val communalSceneRepository
get() = kosmos.communalSceneRepository
+ val shadeRepository
+ get() = kosmos.fakeShadeRepository
+
lateinit var underTest: SharedNotificationContainerViewModel
@Before
fun setUp() {
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
movementFlow = MutableStateFlow(BurnInModel())
whenever(aodBurnInViewModel.movement(any())).thenReturn(movementFlow)
underTest = kosmos.sharedNotificationContainerViewModel
@@ -148,7 +152,7 @@
@Test
fun validateMarginStartInSplitShade() =
testScope.runTest {
- overrideResource(R.bool.config_use_split_notification_shade, true)
+ shadeTestUtil.setSplitShade(true)
overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
val dimens by collectLastValue(underTest.configurationBasedDimensions)
@@ -161,7 +165,7 @@
@Test
fun validateMarginStart() =
testScope.runTest {
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
val dimens by collectLastValue(underTest.configurationBasedDimensions)
@@ -175,7 +179,7 @@
fun validatePaddingTopInSplitShade_usesLargeHeaderHelper() =
testScope.runTest {
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
- overrideResource(R.bool.config_use_split_notification_shade, true)
+ shadeTestUtil.setSplitShade(true)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
overrideResource(R.dimen.large_screen_shade_header_height, 10)
overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
@@ -191,7 +195,7 @@
fun validatePaddingTopInNonSplitShade_usesLargeScreenHeader() =
testScope.runTest {
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(10)
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
overrideResource(R.dimen.large_screen_shade_header_height, 10)
overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
@@ -207,7 +211,7 @@
fun validatePaddingTopInNonSplitShade_doesNotUseLargeScreenHeader() =
testScope.runTest {
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(10)
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
overrideResource(R.bool.config_use_large_screen_shade_header, false)
overrideResource(R.dimen.large_screen_shade_header_height, 10)
overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
@@ -508,7 +512,7 @@
val bounds by collectLastValue(underTest.bounds)
// When not in split shade
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
configurationRepository.onAnyConfigurationChange()
runCurrent()
@@ -567,7 +571,7 @@
// When in split shade
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
- overrideResource(R.bool.config_use_split_notification_shade, true)
+ shadeTestUtil.setSplitShade(true)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
overrideResource(R.dimen.large_screen_shade_header_height, 10)
overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
@@ -628,7 +632,7 @@
advanceTimeBy(50L)
showLockscreen()
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
configurationRepository.onAnyConfigurationChange()
assertThat(maxNotifications).isEqualTo(10)
@@ -656,7 +660,7 @@
advanceTimeBy(50L)
showLockscreen()
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
configurationRepository.onAnyConfigurationChange()
assertThat(maxNotifications).isEqualTo(10)
@@ -690,7 +694,7 @@
// Show lockscreen with shade expanded
showLockscreenWithShadeExpanded()
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeTestUtil.setSplitShade(false)
configurationRepository.onAnyConfigurationChange()
// -1 means No Limit
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
index ab184ab..f232d52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.volume.panel.domain.unavailableCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import com.android.systemui.volume.panel.ui.composable.enabledComponents
+import com.android.systemui.volume.shared.volumePanelLogger
import com.google.common.truth.Truth.assertThat
import javax.inject.Provider
import kotlinx.coroutines.test.runTest
@@ -49,6 +50,7 @@
enabledComponents,
{ defaultCriteria },
testScope.backgroundScope,
+ volumePanelLogger,
criteriaByKey,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
index 420b955..51a70bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
@@ -24,21 +24,30 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.statusbar.policy.fakeConfigurationController
import com.android.systemui.testKosmos
+import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory
import com.android.systemui.volume.panel.data.repository.volumePanelGlobalStateRepository
import com.android.systemui.volume.panel.domain.interactor.criteriaByKey
+import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
import com.android.systemui.volume.panel.domain.unavailableCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import com.android.systemui.volume.panel.shared.model.mockVolumePanelUiComponentProvider
import com.android.systemui.volume.panel.ui.composable.componentByKey
import com.android.systemui.volume.panel.ui.layout.DefaultComponentsLayoutManager
import com.android.systemui.volume.panel.ui.layout.componentsLayoutManager
+import com.android.systemui.volume.shared.volumePanelLogger
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -55,6 +64,7 @@
volumePanelGlobalStateRepository.updateVolumePanelState { it.copy(isVisible = true) }
}
+ private val realDumpManager = DumpManager()
private val testableResources = context.orCreateTestableResources
private lateinit var underTest: VolumePanelViewModel
@@ -124,6 +134,60 @@
}
@Test
+ fun testDumpableRegister_unregister() =
+ with(kosmos) {
+ testScope.runTest {
+ val job = launch {
+ applicationCoroutineScope = this
+ underTest = createViewModel()
+
+ runCurrent()
+
+ assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME })
+ .isTrue()
+ }
+
+ runCurrent()
+ job.cancel()
+
+ assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME }).isTrue()
+ }
+ }
+
+ @Test
+ fun testDumpingState() =
+ test({
+ componentByKey =
+ mapOf(
+ COMPONENT_1 to mockVolumePanelUiComponentProvider,
+ COMPONENT_2 to mockVolumePanelUiComponentProvider,
+ BOTTOM_BAR to mockVolumePanelUiComponentProvider,
+ )
+ criteriaByKey = mapOf(COMPONENT_2 to Provider { unavailableCriteria })
+ }) {
+ testScope.runTest {
+ runCurrent()
+
+ StringWriter().use {
+ underTest.dump(PrintWriter(it), emptyArray())
+
+ assertThat(it.buffer.toString())
+ .isEqualTo(
+ "volumePanelState=" +
+ "VolumePanelState(orientation=1, isLargeScreen=false)\n" +
+ "componentsLayout=( " +
+ "headerComponents= " +
+ "contentComponents=" +
+ "test_component:1:visible=true, " +
+ "test_component:2:visible=false " +
+ "footerComponents= " +
+ "bottomBarComponent=test_bottom_bar:visible=true )\n"
+ )
+ }
+ }
+ }
+
+ @Test
fun dismissBroadcast_dismissesPanel() = test {
testScope.runTest {
runCurrent() // run the flows to let allow the receiver to be registered
@@ -140,11 +204,26 @@
private fun test(setup: Kosmos.() -> Unit = {}, test: Kosmos.() -> Unit) =
with(kosmos) {
setup()
- underTest = volumePanelViewModel
+ underTest = createViewModel()
+
test()
}
+ private fun Kosmos.createViewModel(): VolumePanelViewModel =
+ VolumePanelViewModel(
+ context.orCreateTestableResources.resources,
+ applicationCoroutineScope,
+ volumePanelComponentFactory,
+ configurationController,
+ broadcastDispatcher,
+ realDumpManager,
+ volumePanelLogger,
+ volumePanelGlobalStateInteractor,
+ )
+
private companion object {
+ const val DUMPABLE_NAME = "VolumePanelViewModel"
+
const val BOTTOM_BAR: VolumePanelComponentKey = "test_bottom_bar"
const val COMPONENT_1: VolumePanelComponentKey = "test_component:1"
const val COMPONENT_2: VolumePanelComponentKey = "test_component:2"
diff --git a/packages/SystemUI/res/drawable/ic_bugreport.xml b/packages/SystemUI/res/drawable/ic_bugreport.xml
index ed1c6c7..badbd88 100644
--- a/packages/SystemUI/res/drawable/ic_bugreport.xml
+++ b/packages/SystemUI/res/drawable/ic_bugreport.xml
@@ -19,14 +19,14 @@
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?attr/colorControlNormal">
+ android:tint="?android:attr/colorControlNormal">
<path
- android:fillColor="#FF000000"
+ android:fillColor="#FFFFFFFF"
android:pathData="M20,10V8h-2.81c-0.45,-0.78 -1.07,-1.46 -1.82,-1.96L17,4.41L15.59,3l-2.17,2.17c-0.03,-0.01 -0.05,-0.01 -0.08,-0.01c-0.16,-0.04 -0.32,-0.06 -0.49,-0.09c-0.06,-0.01 -0.11,-0.02 -0.17,-0.03C12.46,5.02 12.23,5 12,5h0c-0.49,0 -0.97,0.07 -1.42,0.18l0.02,-0.01L8.41,3L7,4.41l1.62,1.63l0.01,0C7.88,6.54 7.26,7.22 6.81,8H4v2h2.09C6.03,10.33 6,10.66 6,11v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3h0c2.22,0 4.15,-1.21 5.19,-3H20v-2h-2.09l0,0c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1l0,0H20zM16,15c0,2.21 -1.79,4 -4,4c-2.21,0 -4,-1.79 -4,-4v-4c0,-2.21 1.79,-4 4,-4h0c2.21,0 4,1.79 4,4V15z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="#FFFFFFFF"
android:pathData="M10,14h4v2h-4z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="#FFFFFFFF"
android:pathData="M10,10h4v2h-4z"/>
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/action_key_edu.json b/packages/SystemUI/res/raw/action_key_edu.json
new file mode 100644
index 0000000..014d837
--- /dev/null
+++ b/packages/SystemUI/res/raw/action_key_edu.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":181,"w":554,"h":564,"nm":"Trackpad-JSON_ActionKey-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"BlankButton","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"actionKey_themed","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0.288,-0.035,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.579,-0.158],[0.605,0],[1.21,1.21],[0,1.684],[-1.184,1.184],[-1.684,0],[-1.21,-1.21],[0,-1.71],[0.184,-0.553],[0.316,-0.474],[0,0],[0,0]],"o":[[-0.474,0.316],[-0.553,0.158],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.184],[0,0.605],[-0.158,0.553],[0,0],[0,0],[0,0]],"v":[[10.241,12.155],[8.663,12.866],[6.926,13.103],[2.585,11.287],[0.809,6.946],[2.585,2.605],[6.926,0.789],[11.307,2.605],[13.122,6.946],[12.846,8.682],[12.136,10.222],[16.911,14.997],[15.017,16.891]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.184],[-1.684,0],[-1.184,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0],[1.21,1.184],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,11.327],[-16.911,6.985],[-15.096,2.605],[-10.754,0.789],[-6.374,2.605],[-4.558,6.985],[-6.374,11.327],[-10.754,13.142]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[-8.268,9.432],[-7.242,6.985],[-8.268,4.499],[-10.754,3.473],[-13.201,4.499],[-14.188,6.985],[-13.201,9.432],[-10.754,10.419]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[9.413,9.432],[10.439,6.985],[9.413,4.499],[6.926,3.473],[4.479,4.499],[3.453,6.985],[4.479,9.432],[6.926,10.419]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0],[1.21,1.21],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,-6.354],[-16.911,-10.695],[-15.096,-15.076],[-10.754,-16.891],[-6.374,-15.076],[-4.558,-10.695],[-6.374,-6.354],[-10.754,-4.539]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0]],"v":[[2.585,-6.354],[0.77,-10.695],[2.585,-15.076],[6.926,-16.891],[11.307,-15.076],[13.122,-10.695],[11.307,-6.354],[6.926,-4.539]],"c":true}},"nm":"Path 6","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[-8.268,-8.248],[-7.242,-10.695],[-8.268,-13.182],[-10.754,-14.208],[-13.201,-13.182],[-14.188,-10.695],[-13.201,-8.248],[-10.754,-7.222]],"c":true}},"nm":"Path 7","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[9.413,-8.248],[10.439,-10.695],[9.413,-13.182],[6.926,-14.208],[4.479,-13.182],[3.453,-10.695],[4.479,-8.248],[6.926,-7.222]],"c":true}},"nm":"Path 8","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"icon","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.33],"y":[0.52]},"t":34,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.36],"y":[0]},"t":37,"s":[100]},{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":40,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":46,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.33],"y":[0.52]},"t":124,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.36],"y":[0]},"t":127,"s":[100]},{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":130,"s":[100]},{"t":136,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.92549020052,0.752941191196,0.423529416323,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.072,"y":0.635},"o":{"x":0.424,"y":0.112},"t":27,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.313,"y":0.131},"t":39,"s":[40,49.21,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.07,"y":0.63},"o":{"x":0.42,"y":0.11},"t":117,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.313,"y":0.131},"t":129,"s":[40,49.21,0],"to":[0,0,0],"ti":[0,0,0]},{"t":147,"s":[40,39.79,0]}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"AllApps_Tray_themed","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-8.859,0]},"a":{"a":0,"k":[277,256.562,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-129.938,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[275.625,25.594]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"560x52","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-151.594,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[15.75,1.969]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"handle","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":45,"s":[277,516,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.214,"y":0.214},"t":75,"s":[277,265.422,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.999,"y":1},"o":{"x":0.3,"y":0},"t":135,"s":[277,265.422,0],"to":[0,0,0],"ti":[0,0,0]},{"t":147,"s":[277,516,0]}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[316.969,320.906]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":13.78},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"all apps","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"AK_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Scale Down","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":45,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":51,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":135,"s":[50]},{"t":144,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.153,0.153,0.153],"y":[0.074,0.074,0]},"t":45,"s":[100,100,100]},{"i":{"x":[0.841,0.841,0.841],"y":[1,1,1]},"o":{"x":[0.161,0.161,0.161],"y":[0,0,0]},"t":75,"s":[95,95,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":135,"s":[95,95,100]},{"t":165,"s":[100,100,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"shapes":[],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[133,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[229,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[421,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"actionKey_themed","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[325,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"AllApps_Tray_themed","tt":1,"tp":5,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"AK_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":50},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/action_key_success.json b/packages/SystemUI/res/raw/action_key_success.json
new file mode 100644
index 0000000..cae7344
--- /dev/null
+++ b/packages/SystemUI/res/raw/action_key_success.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":50,"w":554,"h":564,"nm":"Trackpad-JSON_ActionKey-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}]},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[95.049,95.049,100]}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}]},"p":{"a":0,"k":[81,127,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}]}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":-0.289},"p":{"a":0,"k":[14.364,-33.591,0]},"a":{"a":0,"k":[-0.125,0,0]},"s":{"a":0,"k":[104.744,104.744,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":11},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 1","bm":0,"hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[95,95,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":88},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Checkbox - Widget","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"actionKey_themed-static","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0.288,-0.035,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.579,-0.158],[0.605,0],[1.21,1.21],[0,1.684],[-1.184,1.184],[-1.684,0],[-1.21,-1.21],[0,-1.71],[0.184,-0.553],[0.316,-0.474],[0,0],[0,0]],"o":[[-0.474,0.316],[-0.553,0.158],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.184],[0,0.605],[-0.158,0.553],[0,0],[0,0],[0,0]],"v":[[10.241,12.155],[8.663,12.866],[6.926,13.103],[2.585,11.287],[0.809,6.946],[2.585,2.605],[6.926,0.789],[11.307,2.605],[13.122,6.946],[12.846,8.682],[12.136,10.222],[16.911,14.997],[15.017,16.891]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.184],[-1.684,0],[-1.184,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0],[1.21,1.184],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,11.327],[-16.911,6.985],[-15.096,2.605],[-10.754,0.789],[-6.374,2.605],[-4.558,6.985],[-6.374,11.327],[-10.754,13.142]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[-8.268,9.432],[-7.242,6.985],[-8.268,4.499],[-10.754,3.473],[-13.201,4.499],[-14.188,6.985],[-13.201,9.432],[-10.754,10.419]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[9.413,9.432],[10.439,6.985],[9.413,4.499],[6.926,3.473],[4.479,4.499],[3.453,6.985],[4.479,9.432],[6.926,10.419]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0],[1.21,1.21],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,-6.354],[-16.911,-10.695],[-15.096,-15.076],[-10.754,-16.891],[-6.374,-15.076],[-4.558,-10.695],[-6.374,-6.354],[-10.754,-4.539]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0]],"v":[[2.585,-6.354],[0.77,-10.695],[2.585,-15.076],[6.926,-16.891],[11.307,-15.076],[13.122,-10.695],[11.307,-6.354],[6.926,-4.539]],"c":true}},"nm":"Path 6","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[-8.268,-8.248],[-7.242,-10.695],[-8.268,-13.182],[-10.754,-14.208],[-13.201,-13.182],[-14.188,-10.695],[-13.201,-8.248],[-10.754,-7.222]],"c":true}},"nm":"Path 7","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[9.413,-8.248],[10.439,-10.695],[9.413,-13.182],[6.926,-14.208],[4.479,-13.182],[3.453,-10.695],[4.479,-8.248],[6.926,-7.222]],"c":true}},"nm":"Path 8","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"icon","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.92549020052,0.752941191196,0.423529416323,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"BlankButton","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"AK_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Scale Down","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":45,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":51,"s":[70]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":135,"s":[70]},{"t":144,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.153,0.153,0.153],"y":[0.074,0.074,0]},"t":45,"s":[100,100,100]},{"i":{"x":[0.841,0.841,0.841],"y":[1,1,1]},"o":{"x":[0.161,0.161,0.161],"y":[0,0,0]},"t":75,"s":[95,95,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":135,"s":[95,95,100]},{"t":165,"s":[100,100,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"shapes":[],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"TrackpadAK_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,198.5,0]},"a":{"a":0,"k":[95,95,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":190,"h":190,"ip":6,"op":50,"st":6,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"actionKey_themed-static","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[325,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[421,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[229,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[133,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"AK_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":50},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8751596..a5fd5b9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -971,8 +971,8 @@
<string name="hearing_devices_presets_error">Couldn\'t update preset</string>
<!-- QuickSettings: Title for hearing aids presets. Preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=40]-->
<string name="hearing_devices_preset_label">Preset</string>
- <!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40]-->
- <string name="live_caption_title">Live Caption</string>
+ <!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40] [BACKUP_MESSAGE_ID=8916875614623730005]-->
+ <string name="quick_settings_hearing_devices_live_caption_title">Live Caption</string>
<!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] -->
<string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index 317201d..f358ba2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -125,6 +125,7 @@
taskbarMarginLeft, taskbarMarginBottom, floatingRotationButtonPositionLeft);
final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
+ mKeyButtonView.setDiameter(diameter);
mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
taskbarMarginBottom));
}
@@ -195,6 +196,7 @@
public void updateIcon(int lightIconColor, int darkIconColor) {
mAnimatedDrawable = (AnimatedVectorDrawable) mKeyButtonView.getContext()
.getDrawable(mRotationButtonController.getIconResId());
+ mAnimatedDrawable.setBounds(0, 0, mKeyButtonView.getWidth(), mKeyButtonView.getHeight());
mKeyButtonView.setImageDrawable(mAnimatedDrawable);
mKeyButtonView.setColors(lightIconColor, darkIconColor);
}
@@ -248,8 +250,14 @@
updateDimensionResources();
if (mIsShowing) {
+ updateIcon(mRotationButtonController.getLightIconColor(),
+ mRotationButtonController.getDarkIconColor());
final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
+ if (mAnimatedDrawable != null) {
+ mAnimatedDrawable.reset();
+ mAnimatedDrawable.start();
+ }
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index 2145166..75412f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -37,6 +37,7 @@
private static final float BACKGROUND_ALPHA = 0.92f;
private KeyButtonRipple mRipple;
+ private int mDiameter;
private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final Configuration mLastConfiguration;
@@ -93,10 +94,25 @@
mRipple.setDarkIntensity(darkIntensity);
}
+ /**
+ * Sets the view's diameter.
+ *
+ * @param diameter the diameter value for the view
+ */
+ void setDiameter(int diameter) {
+ mDiameter = diameter;
+ }
+
@Override
public void draw(Canvas canvas) {
int d = Math.min(getWidth(), getHeight());
canvas.drawOval(0, 0, d, d, mOvalBgPaint);
super.draw(canvas);
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(mDiameter, mDiameter);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 083f1db..d08653c3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -228,7 +228,7 @@
mHearingDeviceItemList = getHearingDevicesList();
if (mPresetsController != null) {
activeHearingDevice = getActiveHearingDevice(mHearingDeviceItemList);
- mPresetsController.setActiveHearingDevice(activeHearingDevice);
+ mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
} else {
activeHearingDevice = null;
}
@@ -336,7 +336,7 @@
}
final CachedBluetoothDevice activeHearingDevice = getActiveHearingDevice(
mHearingDeviceItemList);
- mPresetsController.setActiveHearingDevice(activeHearingDevice);
+ mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
mPresetInfoAdapter = new ArrayAdapter<>(dialog.getContext(),
R.layout.hearing_devices_preset_spinner_selected,
@@ -499,7 +499,8 @@
final List<ResolveInfo> resolved = packageManager.queryIntentActivities(LIVE_CAPTION_INTENT,
/* flags= */ 0);
if (!resolved.isEmpty()) {
- return new ToolItem(context.getString(R.string.live_caption_title),
+ return new ToolItem(
+ context.getString(R.string.quick_settings_hearing_devices_live_caption_title),
context.getDrawable(R.drawable.ic_volume_odi_captions),
LIVE_CAPTION_INTENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
index b46b8fe..664f3f8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
@@ -27,6 +27,7 @@
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.settingslib.Utils;
import com.android.systemui.bluetooth.qsdialog.DeviceItem;
import com.android.systemui.res.R;
@@ -105,6 +106,7 @@
private final TextView mNameView;
private final TextView mSummaryView;
private final ImageView mIconView;
+ private final ImageView mGearIcon;
private final View mGearView;
DeviceItemViewHolder(@NonNull View itemView, Context context) {
@@ -114,6 +116,7 @@
mNameView = itemView.requireViewById(R.id.bluetooth_device_name);
mSummaryView = itemView.requireViewById(R.id.bluetooth_device_summary);
mIconView = itemView.requireViewById(R.id.bluetooth_device_icon);
+ mGearIcon = itemView.requireViewById(R.id.gear_icon_image);
mGearView = itemView.requireViewById(R.id.gear_icon);
}
@@ -124,13 +127,31 @@
if (backgroundResId != null) {
mContainer.setBackground(mContext.getDrawable(item.getBackground()));
}
- mNameView.setText(item.getDeviceName());
- mSummaryView.setText(item.getConnectionSummary());
+
+ // tint different color in different state for bad color contrast problem
+ int tintColor = item.isActive() ? Utils.getColorAttr(mContext,
+ com.android.internal.R.attr.materialColorOnPrimaryContainer).getDefaultColor()
+ : Utils.getColorAttr(mContext,
+ com.android.internal.R.attr.materialColorOnSurface).getDefaultColor();
+
Pair<Drawable, String> iconPair = item.getIconWithDescription();
if (iconPair != null) {
- mIconView.setImageDrawable(iconPair.getFirst());
+ Drawable drawable = iconPair.getFirst().mutate();
+ drawable.setTint(tintColor);
+ mIconView.setImageDrawable(drawable);
mIconView.setContentDescription(iconPair.getSecond());
}
+
+ mNameView.setTextAppearance(
+ item.isActive() ? R.style.BluetoothTileDialog_DeviceName_Active
+ : R.style.BluetoothTileDialog_DeviceName);
+ mNameView.setText(item.getDeviceName());
+ mSummaryView.setTextAppearance(
+ item.isActive() ? R.style.BluetoothTileDialog_DeviceSummary_Active
+ : R.style.BluetoothTileDialog_DeviceSummary);
+ mSummaryView.setText(item.getConnectionSummary());
+
+ mGearIcon.getDrawable().mutate().setTint(tintColor);
mGearView.setOnClickListener(view -> callback.onDeviceItemGearClicked(item, view));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
index f81124e..aa95fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
@@ -113,7 +113,7 @@
@Override
public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
@@ -137,7 +137,7 @@
@Override
public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
@@ -177,22 +177,33 @@
}
/**
- * Sets the hearing device for this controller to control the preset.
+ * Sets the hearing device for this controller to control the preset if it supports
+ * {@link HapClientProfile}.
*
* @param activeHearingDevice the {@link CachedBluetoothDevice} need to be hearing aid device
+ * and support {@link HapClientProfile}.
*/
- public void setActiveHearingDevice(CachedBluetoothDevice activeHearingDevice) {
- mActiveHearingDevice = activeHearingDevice;
+ public void setHearingDeviceIfSupportHap(CachedBluetoothDevice activeHearingDevice) {
+ if (mHapClientProfile == null || activeHearingDevice == null) {
+ mActiveHearingDevice = null;
+ return;
+ }
+ if (activeHearingDevice.getProfiles().stream().anyMatch(
+ profile -> profile instanceof HapClientProfile)) {
+ mActiveHearingDevice = activeHearingDevice;
+ } else {
+ mActiveHearingDevice = null;
+ }
}
/**
* Selects the currently active preset for {@code mActiveHearingDevice} individual device or
- * the device group accoridng to whether it supports synchronized presets or not.
+ * the device group according to whether it supports synchronized presets or not.
*
* @param presetIndex an index of one of the available presets
*/
public void selectPreset(int presetIndex) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
mSelectedPresetIndex = presetIndex;
@@ -217,7 +228,7 @@
* @return a list of all known preset info
*/
public List<BluetoothHapPresetInfo> getAllPresetInfo() {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return emptyList();
}
return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter(
@@ -230,14 +241,14 @@
* @return active preset index
*/
public int getActivePresetIndex() {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
}
return mHapClientProfile.getActivePresetIndex(mActiveHearingDevice.getDevice());
}
private void selectPresetSynchronously(int groupId, int presetIndex) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
if (DEBUG) {
@@ -250,7 +261,7 @@
}
private void selectPresetIndependently(int presetIndex) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
index 096556f..7e2c9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -26,6 +26,7 @@
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractorImpl
+import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
import dagger.Binds
import dagger.Lazy
import dagger.Module
@@ -74,7 +75,7 @@
implLazy.get()
} else {
// No-op implementation when the flag is disabled.
- return NoOpContextualEducationInteractor
+ return NoOpCoreStartable
}
}
@@ -91,6 +92,8 @@
}
@Provides
+ @IntoMap
+ @ClassKey(KeyboardTouchpadEduInteractor::class)
fun provideKeyboardTouchpadEduInteractor(
implLazy: Lazy<KeyboardTouchpadEduInteractor>
): CoreStartable {
@@ -98,22 +101,32 @@
implLazy.get()
} else {
// No-op implementation when the flag is disabled.
- return NoOpKeyboardTouchpadEduInteractor
+ return NoOpCoreStartable
+ }
+ }
+
+ @Provides
+ @IntoMap
+ @ClassKey(ContextualEduUiCoordinator::class)
+ fun provideContextualEduUiCoordinator(
+ implLazy: Lazy<ContextualEduUiCoordinator>
+ ): CoreStartable {
+ return if (Flags.keyboardTouchpadContextualEducation()) {
+ implLazy.get()
+ } else {
+ // No-op implementation when the flag is disabled.
+ return NoOpCoreStartable
}
}
}
+}
- private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
- override fun incrementSignalCount(gestureType: GestureType) {}
+private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
+ override fun incrementSignalCount(gestureType: GestureType) {}
- override fun updateShortcutTriggerTime(gestureType: GestureType) {}
- }
+ override fun updateShortcutTriggerTime(gestureType: GestureType) {}
+}
- private object NoOpContextualEducationInteractor : CoreStartable {
- override fun start() {}
- }
-
- private object NoOpKeyboardTouchpadEduInteractor : CoreStartable {
- override fun start() {}
- }
+private object NoOpCoreStartable : CoreStartable {
+ override fun start() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
new file mode 100644
index 0000000..b446ea2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024 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.education.ui.view
+
+import android.content.Context
+import android.widget.Toast
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.education.shared.model.EducationUiType
+import com.android.systemui.education.ui.viewmodel.ContextualEduContentViewModel
+import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A class to show contextual education on UI based on the edu produced from
+ * [ContextualEduViewModel]
+ */
+@SysUISingleton
+class ContextualEduUiCoordinator
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val viewModel: ContextualEduViewModel,
+ private val createToast: (String) -> Toast
+) : CoreStartable {
+
+ @Inject
+ constructor(
+ @Application applicationScope: CoroutineScope,
+ context: Context,
+ viewModel: ContextualEduViewModel,
+ ) : this(
+ applicationScope,
+ viewModel,
+ createToast = { message -> Toast.makeText(context, message, Toast.LENGTH_LONG) }
+ )
+
+ override fun start() {
+ applicationScope.launch {
+ viewModel.eduContent.collect { contentModel ->
+ if (contentModel.type == EducationUiType.Toast) {
+ showToast(contentModel)
+ }
+ }
+ }
+ }
+
+ private fun showToast(model: ContextualEduContentViewModel) {
+ val toast = createToast(model.message)
+ toast.show()
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
similarity index 65%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
index 460de8c..3cba4c8 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 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.
@@ -14,18 +14,8 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package com.android.systemui.education.ui.viewmodel
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+import com.android.systemui.education.shared.model.EducationUiType
- /** carrier id */
- int mCarrierId;
-
- /** apn */
- String mNiddApn;
-}
+data class ContextualEduContentViewModel(val message: String, val type: EducationUiType)
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
new file mode 100644
index 0000000..58276e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 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.education.ui.viewmodel
+
+import android.content.res.Resources
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
+import com.android.systemui.education.shared.model.EducationInfo
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class ContextualEduViewModel
+@Inject
+constructor(@Main private val resources: Resources, interactor: KeyboardTouchpadEduInteractor) {
+ val eduContent: Flow<ContextualEduContentViewModel> =
+ interactor.educationTriggered.filterNotNull().map {
+ ContextualEduContentViewModel(getEduContent(it), it.educationUiType)
+ }
+
+ private fun getEduContent(educationInfo: EducationInfo): String {
+ // Todo: also check UiType in educationInfo to determine the string
+ val resourceId =
+ when (educationInfo.gestureType) {
+ GestureType.BACK -> R.string.back_edu_toast_content
+ GestureType.HOME -> R.string.home_edu_toast_content
+ GestureType.OVERVIEW -> R.string.overview_edu_toast_content
+ GestureType.ALL_APPS -> R.string.all_apps_edu_toast_content
+ }
+ return resources.getString(resourceId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
index cfe64e2..9f46846 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
@@ -16,11 +16,6 @@
package com.android.systemui.inputdevice.tutorial.data.model
-data class TutorialSchedulerInfo(
- val keyboard: DeviceSchedulerInfo = DeviceSchedulerInfo(),
- val touchpad: DeviceSchedulerInfo = DeviceSchedulerInfo()
-)
-
data class DeviceSchedulerInfo(var isLaunched: Boolean = false, var connectTime: Long? = null) {
val wasEverConnected: Boolean
get() = connectTime != null
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
index 31ff018..b9b3895 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
@@ -25,21 +25,32 @@
import androidx.datastore.preferences.preferencesDataStore
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo
-import com.android.systemui.inputdevice.tutorial.data.model.TutorialSchedulerInfo
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
@SysUISingleton
class TutorialSchedulerRepository
@Inject
-constructor(@Application private val applicationContext: Context) {
+constructor(
+ @Application private val applicationContext: Context,
+ @Background private val backgroundScope: CoroutineScope
+) {
private val Context.dataStore: DataStore<Preferences> by
- preferencesDataStore(name = DATASTORE_NAME)
+ preferencesDataStore(name = DATASTORE_NAME, scope = backgroundScope)
- suspend fun loadData(): TutorialSchedulerInfo {
+ suspend fun isLaunched(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isLaunched
+
+ suspend fun wasEverConnected(deviceType: DeviceType): Boolean =
+ loadData()[deviceType]!!.wasEverConnected
+
+ suspend fun connectTime(deviceType: DeviceType): Long = loadData()[deviceType]!!.connectTime!!
+
+ private suspend fun loadData(): Map<DeviceType, DeviceSchedulerInfo> {
return applicationContext.dataStore.data.map { pref -> getSchedulerInfo(pref) }.first()
}
@@ -51,10 +62,10 @@
applicationContext.dataStore.edit { pref -> pref[getLaunchedKey(device)] = true }
}
- private fun getSchedulerInfo(pref: Preferences): TutorialSchedulerInfo {
- return TutorialSchedulerInfo(
- keyboard = getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
- touchpad = getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD)
+ private fun getSchedulerInfo(pref: Preferences): Map<DeviceType, DeviceSchedulerInfo> {
+ return mapOf(
+ DeviceType.KEYBOARD to getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
+ DeviceType.TOUCHPAD to getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index 05e1044..b3b8f21 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -16,23 +16,25 @@
package com.android.systemui.inputdevice.tutorial.domain.interactor
-import android.content.Context
-import android.content.Intent
+import android.os.SystemProperties
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
import com.android.systemui.keyboard.data.repository.KeyboardRepository
import com.android.systemui.touchpad.data.repository.TouchpadRepository
-import java.time.Duration
import java.time.Instant
import javax.inject.Inject
+import kotlin.time.Duration.Companion.hours
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
/**
@@ -43,62 +45,72 @@
class TutorialSchedulerInteractor
@Inject
constructor(
- @Application private val context: Context,
- @Application private val applicationScope: CoroutineScope,
- private val keyboardRepository: KeyboardRepository,
- private val touchpadRepository: TouchpadRepository,
- private val tutorialSchedulerRepository: TutorialSchedulerRepository
+ @Background private val backgroundScope: CoroutineScope,
+ keyboardRepository: KeyboardRepository,
+ touchpadRepository: TouchpadRepository,
+ private val repo: TutorialSchedulerRepository
) {
+ private val isAnyDeviceConnected =
+ mapOf(
+ KEYBOARD to keyboardRepository.isAnyKeyboardConnected,
+ TOUCHPAD to touchpadRepository.isAnyTouchpadConnected
+ )
+
fun start() {
- applicationScope.launch {
- val info = tutorialSchedulerRepository.loadData()
- if (!info.keyboard.isLaunched) {
- applicationScope.launch {
- schedule(
- keyboardRepository.isAnyKeyboardConnected,
- info.keyboard,
- DeviceType.KEYBOARD
- )
- }
- }
- if (!info.touchpad.isLaunched) {
- applicationScope.launch {
- schedule(
- touchpadRepository.isAnyTouchpadConnected,
- info.touchpad,
- DeviceType.TOUCHPAD
- )
- }
+ backgroundScope.launch {
+ // Merging two flows to ensure that launch tutorial is launched consecutively in order
+ // to avoid race condition
+ merge(touchpadScheduleFlow, keyboardScheduleFlow).collect {
+ val tutorialType = resolveTutorialType(it)
+ launchTutorial(tutorialType)
}
}
}
- private suspend fun schedule(
- isAnyDeviceConnected: Flow<Boolean>,
- info: DeviceSchedulerInfo,
- deviceType: DeviceType
- ) {
- if (!info.wasEverConnected) {
- waitForDeviceConnection(isAnyDeviceConnected)
- info.connectTime = Instant.now().toEpochMilli()
- tutorialSchedulerRepository.updateConnectTime(deviceType, info.connectTime!!)
+ private val touchpadScheduleFlow = flow {
+ if (!repo.isLaunched(TOUCHPAD)) {
+ schedule(TOUCHPAD)
+ emit(TOUCHPAD)
}
- delay(remainingTimeMillis(info.connectTime!!))
- waitForDeviceConnection(isAnyDeviceConnected)
- info.isLaunched = true
- tutorialSchedulerRepository.updateLaunch(deviceType)
- launchTutorial()
}
- private suspend fun waitForDeviceConnection(isAnyDeviceConnected: Flow<Boolean>): Boolean {
- return isAnyDeviceConnected.filter { it }.first()
+ private val keyboardScheduleFlow = flow {
+ if (!repo.isLaunched(KEYBOARD)) {
+ schedule(KEYBOARD)
+ emit(KEYBOARD)
+ }
}
- private fun launchTutorial() {
- val intent = Intent(TUTORIAL_ACTION)
- intent.addCategory(Intent.CATEGORY_DEFAULT)
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- context.startActivity(intent)
+ private suspend fun schedule(deviceType: DeviceType) {
+ if (!repo.wasEverConnected(deviceType)) {
+ waitForDeviceConnection(deviceType)
+ repo.updateConnectTime(deviceType, Instant.now().toEpochMilli())
+ }
+ delay(remainingTimeMillis(start = repo.connectTime(deviceType)))
+ waitForDeviceConnection(deviceType)
+ }
+
+ private suspend fun waitForDeviceConnection(deviceType: DeviceType) =
+ isAnyDeviceConnected[deviceType]!!.filter { it }.first()
+
+ private suspend fun launchTutorial(tutorialType: TutorialType) {
+ if (tutorialType == TutorialType.KEYBOARD || tutorialType == TutorialType.BOTH)
+ repo.updateLaunch(KEYBOARD)
+ if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH)
+ repo.updateLaunch(TOUCHPAD)
+ // TODO: launch tutorial
+ Log.d(TAG, "Launch tutorial for $tutorialType")
+ }
+
+ private suspend fun resolveTutorialType(deviceType: DeviceType): TutorialType {
+ // Resolve the type of tutorial depending on which device are connected when the tutorial is
+ // launched. E.g. when the keyboard is connected for [LAUNCH_DELAY], both keyboard and
+ // touchpad are connected, we launch the tutorial for both.
+ if (repo.isLaunched(deviceType)) return TutorialType.NONE
+ val otherDevice = if (deviceType == KEYBOARD) TOUCHPAD else KEYBOARD
+ val isOtherDeviceConnected = isAnyDeviceConnected[otherDevice]!!.first()
+ if (!repo.isLaunched(otherDevice) && isOtherDeviceConnected) return TutorialType.BOTH
+ return if (deviceType == KEYBOARD) TutorialType.KEYBOARD else TutorialType.TOUCHPAD
}
private fun remainingTimeMillis(start: Long): Long {
@@ -107,7 +119,20 @@
}
companion object {
- const val TUTORIAL_ACTION = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
- private val LAUNCH_DELAY = Duration.ofHours(72).toMillis()
+ const val TAG = "TutorialSchedulerInteractor"
+ private val DEFAULT_LAUNCH_DELAY = 72.hours.inWholeMilliseconds
+ private val LAUNCH_DELAY: Long
+ get() =
+ SystemProperties.getLong(
+ "persist.peripheral_tutorial_delay_ms",
+ DEFAULT_LAUNCH_DELAY
+ )
+ }
+
+ enum class TutorialType {
+ KEYBOARD,
+ TOUCHPAD,
+ BOTH,
+ NONE
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 9f33113..871d046 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -645,6 +645,9 @@
public void showDismissibleKeyguard() {
trace("showDismissibleKeyguard");
checkPermission();
+ if (mFoldGracePeriodProvider.get().isEnabled()) {
+ mKeyguardInteractor.showDismissibleKeyguard();
+ }
mKeyguardViewMediator.showDismissibleKeyguard();
if (SceneContainerFlag.isEnabled() && mFoldGracePeriodProvider.get().isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0b3d0f7..3f9c98d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2399,6 +2399,16 @@
*/
private void handleDismiss(IKeyguardDismissCallback callback, CharSequence message) {
if (mShowing) {
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ Log.d(TAG, "Dismissing keyguard with keyguard_wm_refactor_enabled: "
+ + "cancelDoKeyguardLaterLocked");
+
+ // This won't get canceled in onKeyguardExitFinished() if the refactor is enabled,
+ // which can lead to the keyguard re-showing. Cancel here for now; this can be
+ // removed once we migrate the logic that posts doKeyguardLater in the first place.
+ cancelDoKeyguardLaterLocked();
+ }
+
if (callback != null) {
mDismissCallbackRegistry.addCallback(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index edf17c1..81b0064 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -232,6 +232,9 @@
/** Receive an event for doze time tick */
val dozeTimeTick: Flow<Long>
+ /** Receive an event lockscreen being shown in a dismissible state */
+ val showDismissibleKeyguard: MutableStateFlow<Long>
+
/** Observable for DismissAction */
val dismissAction: StateFlow<DismissAction>
@@ -305,6 +308,8 @@
fun dozeTimeTick()
+ fun showDismissibleKeyguard()
+
fun setDismissAction(dismissAction: DismissAction)
suspend fun setKeyguardDone(keyguardDoneType: KeyguardDone)
@@ -439,6 +444,12 @@
_dozeTimeTick.value = systemClock.uptimeMillis()
}
+ override val showDismissibleKeyguard = MutableStateFlow<Long>(0L)
+
+ override fun showDismissibleKeyguard() {
+ showDismissibleKeyguard.value = systemClock.uptimeMillis()
+ }
+
private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 8f4110c..db5a63b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -74,6 +74,7 @@
listenForGoneToAodOrDozing()
listenForGoneToDreaming()
listenForGoneToLockscreenOrHub()
+ listenForGoneToOccluded()
listenForGoneToDreamingLockscreenHosted()
}
@@ -81,6 +82,27 @@
scope.launch("$TAG#showKeyguard") { startTransitionTo(KeyguardState.LOCKSCREEN) }
}
+ /**
+ * A special case supported on foldables, where folding the device may put the device on an
+ * unlocked lockscreen, but if an occluding app is already showing (like a active phone call),
+ * then go directly to OCCLUDED.
+ */
+ private fun listenForGoneToOccluded() {
+ scope.launch("$TAG#listenForGoneToOccluded") {
+ keyguardInteractor.showDismissibleKeyguard
+ .filterRelevantKeyguardState()
+ .sample(keyguardInteractor.isKeyguardOccluded, ::Pair)
+ .collect { (_, isKeyguardOccluded) ->
+ if (isKeyguardOccluded) {
+ startTransitionTo(
+ KeyguardState.OCCLUDED,
+ ownerReason = "Dismissible keyguard with occlusion"
+ )
+ }
+ }
+ }
+ }
+
// Primarily for when the user chooses to lock down the device
private fun listenForGoneToLockscreenOrHub() {
if (KeyguardWmStateRefactor.isEnabled) {
@@ -166,11 +188,12 @@
interpolator = Interpolators.LINEAR
duration =
when (toState) {
- KeyguardState.DREAMING -> TO_DREAMING_DURATION
KeyguardState.AOD -> TO_AOD_DURATION
KeyguardState.DOZING -> TO_DOZING_DURATION
+ KeyguardState.DREAMING -> TO_DREAMING_DURATION
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
+ KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -179,10 +202,11 @@
companion object {
private const val TAG = "FromGoneTransitionInteractor"
private val DEFAULT_DURATION = 500.milliseconds
- val TO_DREAMING_DURATION = 933.milliseconds
val TO_AOD_DURATION = 1300.milliseconds
val TO_DOZING_DURATION = 933.milliseconds
+ val TO_DREAMING_DURATION = 933.milliseconds
val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
+ val TO_OCCLUDED_DURATION = 100.milliseconds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 51d92f0..5dc020f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -300,7 +300,9 @@
swipeToDismissInteractor.dismissFling
.filterNotNull()
.filterRelevantKeyguardState()
- .collect { _ -> startTransitionTo(KeyguardState.GONE) }
+ .collect { _ ->
+ startTransitionTo(KeyguardState.GONE, ownerReason = "dismissFling != null")
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 710b710a..aea57ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -157,6 +157,13 @@
}
}
+ /** Starts a transition to dismiss the keyguard from the OCCLUDED state. */
+ fun dismissFromOccluded() {
+ scope.launch {
+ startTransitionTo(KeyguardState.GONE, ownerReason = "Dismiss from occluded")
+ }
+ }
+
private fun listenForOccludedToGone() {
if (KeyguardWmStateRefactor.isEnabled) {
// We don't think OCCLUDED to GONE is possible. You should always have to go via a
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 69e10d9..0df989e 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
@@ -1,18 +1,17 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2022 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
+ * 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
+ * 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.
- *
+ * 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.
*/
@file:OptIn(ExperimentalCoroutinesApi::class)
@@ -180,6 +179,9 @@
val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> =
repository.onCameraLaunchDetected.filter { it.type != CameraLaunchType.IGNORE }
+ /** Event for when an unlocked keyguard has been requested, such as on device fold */
+ val showDismissibleKeyguard: Flow<Long> = repository.showDismissibleKeyguard.asStateFlow()
+
/**
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
* that doze mode is not running and DREAMING is ok to commence.
@@ -490,6 +492,10 @@
CameraLaunchSourceModel(type = cameraLaunchSourceIntToType(source))
}
+ fun showDismissibleKeyguard() {
+ repository.showDismissibleKeyguard()
+ }
+
companion object {
private const val TAG = "KeyguardInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index afbe357..efdae62 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -75,6 +75,7 @@
private val fromAlternateBouncerTransitionInteractor:
dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
+ private val fromOccludedTransitionInteractor: dagger.Lazy<FromOccludedTransitionInteractor>,
private val sceneInteractor: SceneInteractor,
) {
private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>()
@@ -418,6 +419,7 @@
fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer()
AOD -> fromAodTransitionInteractor.get().dismissAod()
DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing()
+ KeyguardState.OCCLUDED -> fromOccludedTransitionInteractor.get().dismissFromOccluded()
KeyguardState.GONE ->
Log.i(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
index 86e4115..906d586 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
@@ -19,14 +19,15 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import javax.inject.Inject
/**
* Handles logic around the swipe to dismiss gesture, where the user swipes up on the dismissable
@@ -53,12 +54,14 @@
shadeRepository.currentFling
.sample(
transitionInteractor.startedKeyguardState,
- keyguardInteractor.isKeyguardDismissible
+ keyguardInteractor.isKeyguardDismissible,
+ keyguardInteractor.statusBarState,
)
- .filter { (flingInfo, startedState, keyguardDismissable) ->
+ .filter { (flingInfo, startedState, keyguardDismissable, statusBarState) ->
flingInfo != null &&
- !flingInfo.expand &&
- startedState == KeyguardState.LOCKSCREEN &&
+ !flingInfo.expand &&
+ statusBarState != StatusBarState.SHADE_LOCKED &&
+ startedState == KeyguardState.LOCKSCREEN &&
keyguardDismissable
}
.map { (flingInfo, _) -> flingInfo }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index e1b333d..25b2b7c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -93,6 +93,14 @@
KeyguardState.ALTERNATE_BOUNCER -> {
fromAlternateBouncerInteractor.surfaceBehindVisibility
}
+ KeyguardState.OCCLUDED -> {
+ // OCCLUDED -> GONE occurs when an app is on top of the keyguard, and then
+ // requests manual dismissal of the keyguard in the background. The app will
+ // remain visible on top of the stack throughout this transition, so we
+ // should not trigger the keyguard going away animation by returning
+ // surfaceBehindVisibility = true.
+ flowOf(false)
+ }
else -> flowOf(null)
}
}
@@ -253,6 +261,18 @@
) {
// Dreams dismiss keyguard and return to GONE if they can.
false
+ } else if (
+ startedWithPrev.newValue.from == KeyguardState.OCCLUDED &&
+ startedWithPrev.newValue.to == KeyguardState.GONE
+ ) {
+ // OCCLUDED -> GONE directly, without transiting a *_BOUNCER state, occurs
+ // when an app uses intent flags to launch over an insecure keyguard without
+ // dismissing it, and then manually requests keyguard dismissal while
+ // OCCLUDED. This transition is not user-visible; the device unlocks in the
+ // background and the app remains on top, while we're now GONE. In this case
+ // we should simply tell WM that the lockscreen is no longer visible, and
+ // *not* play the going away animation or related animations.
+ false
} else {
// Otherwise, use the visibility of the current state.
KeyguardState.lockscreenVisibleInState(currentState)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
index 9dc77d3..fb97191 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ExperimentalCoroutinesApi
@@ -52,7 +53,11 @@
}
}
- launch("$TAG#viewModel.alpha") { viewModel.alpha.collect { view.alpha = it } }
+ if (SceneContainerFlag.isEnabled) {
+ view.alpha = 1f
+ } else {
+ launch("$TAG#viewModel.alpha") { viewModel.alpha.collect { view.alpha = it } }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index a250b22..91a7f7f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -37,13 +37,13 @@
import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
-import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scrim.ScrimView
import dagger.Lazy
import javax.inject.Inject
@@ -67,7 +67,6 @@
private val alternateBouncerDependencies: Lazy<AlternateBouncerDependencies>,
private val windowManager: Lazy<WindowManager>,
private val layoutInflater: Lazy<LayoutInflater>,
- private val dismissCallbackRegistry: DismissCallbackRegistry,
) : CoreStartable {
private val layoutParams: WindowManager.LayoutParams
get() =
@@ -95,9 +94,10 @@
private var alternateBouncerView: ConstraintLayout? = null
override fun start() {
- if (!DeviceEntryUdfpsRefactor.isEnabled) {
+ if (!DeviceEntryUdfpsRefactor.isEnabled || SceneContainerFlag.isEnabled) {
return
}
+
applicationScope.launch("$TAG#alternateBouncerWindowViewModel") {
alternateBouncerWindowViewModel.get().alternateBouncerWindowRequired.collect {
addAlternateBouncerWindowView ->
@@ -110,7 +110,7 @@
bind(alternateBouncerView!!, alternateBouncerDependencies.get())
} else {
removeViewFromWindowManager()
- alternateBouncerDependencies.get().viewModel.hideAlternateBouncer()
+ alternateBouncerDependencies.get().viewModel.onRemovedFromWindow()
}
}
}
@@ -144,7 +144,7 @@
private val onAttachAddBackGestureHandler =
object : View.OnAttachStateChangeListener {
private val onBackInvokedCallback: OnBackInvokedCallback = OnBackInvokedCallback {
- onBackRequested()
+ alternateBouncerDependencies.get().viewModel.onBackRequested()
}
override fun onViewAttachedToWindow(view: View) {
@@ -161,14 +161,12 @@
.findOnBackInvokedDispatcher()
?.unregisterOnBackInvokedCallback(onBackInvokedCallback)
}
-
- fun onBackRequested() {
- alternateBouncerDependencies.get().viewModel.hideAlternateBouncer()
- dismissCallbackRegistry.notifyDismissCancelled()
- }
}
private fun addViewToWindowManager() {
+ if (SceneContainerFlag.isEnabled) {
+ return
+ }
if (alternateBouncerView != null) {
return
}
@@ -190,6 +188,7 @@
if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
return
}
+
optionallyAddUdfpsViews(
view = view,
udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel,
@@ -202,12 +201,13 @@
viewModel = alternateBouncerDependencies.messageAreaViewModel,
)
- val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView
+ val scrim: ScrimView = view.requireViewById(R.id.alternate_bouncer_scrim)
val viewModel = alternateBouncerDependencies.viewModel
val swipeUpAnywhereGestureHandler =
alternateBouncerDependencies.swipeUpAnywhereGestureHandler
val tapGestureDetector = alternateBouncerDependencies.tapGestureDetector
- view.repeatWhenAttached { alternateBouncerViewContainer ->
+
+ view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch("$TAG#viewModel.registerForDismissGestures") {
viewModel.registerForDismissGestures.collect { registerForDismissGestures ->
@@ -216,11 +216,11 @@
swipeTag
) { _ ->
alternateBouncerDependencies.powerInteractor.onUserTouch()
- viewModel.showPrimaryBouncer()
+ viewModel.onTapped()
}
tapGestureDetector.addOnGestureDetectedCallback(tapTag) { _ ->
alternateBouncerDependencies.powerInteractor.onUserTouch()
- viewModel.showPrimaryBouncer()
+ viewModel.onTapped()
}
} else {
swipeUpAnywhereGestureHandler.removeOnGestureDetectedCallback(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 162a0d2..15e6b1d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,18 +30,23 @@
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launch
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.binder.IconViewBinder
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.doOnEnd
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
@@ -49,11 +54,20 @@
import kotlinx.coroutines.launch
/** This is only for a SINGLE Quick affordance */
-object KeyguardQuickAffordanceViewBinder {
+@SysUISingleton
+class KeyguardQuickAffordanceViewBinder
+@Inject
+constructor(
+ private val falsingManager: FalsingManager?,
+ private val vibratorHelper: VibratorHelper?,
+ private val logger: KeyguardQuickAffordancesLogger,
+ @Main private val mainImmediateDispatcher: CoroutineDispatcher,
+) {
- private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
- private const val SCALE_SELECTED_BUTTON = 1.23f
- private const val DIM_ALPHA = 0.3f
+ private val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
+ private val SCALE_SELECTED_BUTTON = 1.23f
+ private val DIM_ALPHA = 0.3f
+ private val TAG = "KeyguardQuickAffordanceViewBinder"
/**
* Defines interface for an object that acts as the binding between the view and its view-model.
@@ -73,30 +87,24 @@
view: LaunchableImageView,
viewModel: Flow<KeyguardQuickAffordanceViewModel>,
alpha: Flow<Float>,
- falsingManager: FalsingManager?,
- vibratorHelper: VibratorHelper?,
- logger: KeyguardQuickAffordancesLogger,
messageDisplayer: (Int) -> Unit,
): Binding {
val button = view as ImageView
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
val disposableHandle =
- view.repeatWhenAttached {
+ view.repeatWhenAttached(mainImmediateDispatcher) {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
+ launch("$TAG#viewModel") {
viewModel.collect { buttonModel ->
updateButton(
view = button,
viewModel = buttonModel,
- falsingManager = falsingManager,
messageDisplayer = messageDisplayer,
- vibratorHelper = vibratorHelper,
- logger = logger,
)
}
}
- launch {
+ launch("$TAG#updateButtonAlpha") {
updateButtonAlpha(
view = button,
viewModel = viewModel,
@@ -104,7 +112,7 @@
)
}
- launch {
+ launch("$TAG#configurationBasedDimensions") {
configurationBasedDimensions.collect { dimensions ->
button.updateLayoutParams<ViewGroup.LayoutParams> {
width = dimensions.buttonSizePx.width
@@ -131,10 +139,7 @@
private fun updateButton(
view: ImageView,
viewModel: KeyguardQuickAffordanceViewModel,
- falsingManager: FalsingManager?,
messageDisplayer: (Int) -> Unit,
- vibratorHelper: VibratorHelper?,
- logger: KeyguardQuickAffordancesLogger,
) {
if (!viewModel.isVisible) {
view.isInvisible = true
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 6faca1e..6031ef6 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
@@ -50,7 +50,6 @@
import com.android.internal.policy.SystemBarUtils
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
-import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -79,7 +78,6 @@
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
-import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -89,7 +87,6 @@
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
import com.android.systemui.statusbar.KeyguardIndicationController
-import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -133,8 +130,6 @@
private val broadcastDispatcher: BroadcastDispatcher,
private val lockscreenSmartspaceController: LockscreenSmartspaceController,
private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
- private val falsingManager: FalsingManager,
- private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
private val keyguardRootViewModel: KeyguardRootViewModel,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
@@ -148,7 +143,7 @@
private val defaultShortcutsSection: DefaultShortcutsSection,
private val keyguardClockInteractor: KeyguardClockInteractor,
private val keyguardClockViewModel: KeyguardClockViewModel,
- private val quickAffordancesLogger: KeyguardQuickAffordancesLogger,
+ private val keyguardQuickAffordanceViewBinder: KeyguardQuickAffordanceViewBinder,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
@@ -458,13 +453,10 @@
keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let { imageView ->
shortcutsBindings.add(
- KeyguardQuickAffordanceViewBinder.bind(
+ keyguardQuickAffordanceViewBinder.bind(
view = imageView,
viewModel = quickAffordancesCombinedViewModel.startButton,
alpha = flowOf(1f),
- falsingManager = falsingManager,
- vibratorHelper = vibratorHelper,
- logger = quickAffordancesLogger,
) { message ->
indicationController.showTransientIndication(message)
}
@@ -473,13 +465,10 @@
keyguardRootView.findViewById<LaunchableImageView?>(R.id.end_button)?.let { imageView ->
shortcutsBindings.add(
- KeyguardQuickAffordanceViewBinder.bind(
+ keyguardQuickAffordanceViewBinder.bind(
view = imageView,
viewModel = quickAffordancesCombinedViewModel.endButton,
alpha = flowOf(1f),
- falsingManager = falsingManager,
- vibratorHelper = vibratorHelper,
- logger = quickAffordancesLogger,
) { message ->
indicationController.showTransientIndication(message)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index 2dc9301..bf6f2c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -42,12 +42,6 @@
): KeyguardBlueprint
@Binds
- @IntoSet
- abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
- shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
- ): KeyguardBlueprint
-
- @Binds
@IntoMap
@ClassKey(KeyguardBlueprintInteractor::class)
abstract fun bindsKeyguardBlueprintInteractor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
deleted file mode 100644
index b984a68..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.layout.blueprints
-
-import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
-import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
-import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
-import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntrySection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection
-import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
-import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSliceViewSection
-import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
-import com.android.systemui.util.kotlin.getOrNull
-import java.util.Optional
-import javax.inject.Inject
-import javax.inject.Named
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-/** Vertically aligns the shortcuts with the udfps. */
-@ExperimentalCoroutinesApi
-@SysUISingleton
-class ShortcutsBesideUdfpsKeyguardBlueprint
-@Inject
-constructor(
- accessibilityActionsSection: AccessibilityActionsSection,
- alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
- defaultIndicationAreaSection: DefaultIndicationAreaSection,
- defaultDeviceEntrySection: DefaultDeviceEntrySection,
- @Named(KeyguardSectionsModule.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION)
- defaultAmbientIndicationAreaSection: Optional<KeyguardSection>,
- defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
- defaultStatusViewSection: DefaultStatusViewSection,
- defaultStatusBarSection: DefaultStatusBarSection,
- defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
- aodNotificationIconsSection: AodNotificationIconsSection,
- aodBurnInSection: AodBurnInSection,
- communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
- clockSection: ClockSection,
- smartspaceSection: SmartspaceSection,
- keyguardSliceViewSection: KeyguardSliceViewSection,
- udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection,
-) : KeyguardBlueprint {
- override val id: String = SHORTCUTS_BESIDE_UDFPS
-
- override val sections =
- listOfNotNull(
- accessibilityActionsSection,
- defaultIndicationAreaSection,
- alignShortcutsToUdfpsSection,
- defaultAmbientIndicationAreaSection.getOrNull(),
- defaultSettingsPopupMenuSection,
- defaultStatusViewSection,
- defaultStatusBarSection,
- defaultNotificationStackScrollLayoutSection,
- aodNotificationIconsSection,
- smartspaceSection,
- aodBurnInSection,
- communalTutorialIndicatorSection,
- clockSection,
- keyguardSliceViewSection,
- defaultDeviceEntrySection,
- udfpsAccessibilityOverlaySection, // Add LAST: Intentionally has z-order above others
- )
-
- companion object {
- const val SHORTCUTS_BESIDE_UDFPS = "shortcuts-besides-udfps"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
deleted file mode 100644
index 1ba830b..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.layout.sections
-
-import android.content.res.Resources
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.LEFT
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.RIGHT
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
-import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.KeyguardIndicationController
-import com.android.systemui.statusbar.VibratorHelper
-import javax.inject.Inject
-
-class AlignShortcutsToUdfpsSection
-@Inject
-constructor(
- @Main private val resources: Resources,
- private val keyguardQuickAffordancesCombinedViewModel:
- KeyguardQuickAffordancesCombinedViewModel,
- private val keyguardRootViewModel: KeyguardRootViewModel,
- private val falsingManager: FalsingManager,
- private val indicationController: KeyguardIndicationController,
- private val vibratorHelper: VibratorHelper,
- private val shortcutsLogger: KeyguardQuickAffordancesLogger,
-) : BaseShortcutSection() {
- override fun addViews(constraintLayout: ConstraintLayout) {
- if (KeyguardBottomAreaRefactor.isEnabled) {
- addLeftShortcut(constraintLayout)
- addRightShortcut(constraintLayout)
- }
- }
-
- override fun bindData(constraintLayout: ConstraintLayout) {
- if (KeyguardBottomAreaRefactor.isEnabled) {
- leftShortcutHandle =
- KeyguardQuickAffordanceViewBinder.bind(
- constraintLayout.requireViewById(R.id.start_button),
- keyguardQuickAffordancesCombinedViewModel.startButton,
- keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
- falsingManager,
- vibratorHelper,
- shortcutsLogger,
- ) {
- indicationController.showTransientIndication(it)
- }
- rightShortcutHandle =
- KeyguardQuickAffordanceViewBinder.bind(
- constraintLayout.requireViewById(R.id.end_button),
- keyguardQuickAffordancesCombinedViewModel.endButton,
- keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
- falsingManager,
- vibratorHelper,
- shortcutsLogger,
- ) {
- indicationController.showTransientIndication(it)
- }
- }
- }
-
- override fun applyConstraints(constraintSet: ConstraintSet) {
- val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
- val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
-
- val lockIconViewId =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- R.id.device_entry_icon_view
- } else {
- R.id.lock_icon_view
- }
-
- constraintSet.apply {
- constrainWidth(R.id.start_button, width)
- constrainHeight(R.id.start_button, height)
- connect(R.id.start_button, LEFT, PARENT_ID, LEFT)
- connect(R.id.start_button, RIGHT, lockIconViewId, LEFT)
- connect(R.id.start_button, TOP, lockIconViewId, TOP)
- connect(R.id.start_button, BOTTOM, lockIconViewId, BOTTOM)
-
- constrainWidth(R.id.end_button, width)
- constrainHeight(R.id.end_button, height)
- connect(R.id.end_button, RIGHT, PARENT_ID, RIGHT)
- connect(R.id.end_button, LEFT, lockIconViewId, RIGHT)
- connect(R.id.end_button, TOP, lockIconViewId, TOP)
- connect(R.id.end_button, BOTTOM, lockIconViewId, BOTTOM)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index 64c46db..e558033 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -26,7 +26,6 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.VISIBILITY_MODE_IGNORE
-import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -35,10 +34,8 @@
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
-import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
@@ -49,11 +46,9 @@
private val keyguardQuickAffordancesCombinedViewModel:
KeyguardQuickAffordancesCombinedViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
- private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
- private val vibratorHelper: VibratorHelper,
private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
- private val shortcutsLogger: KeyguardQuickAffordancesLogger,
+ private val keyguardQuickAffordanceViewBinder: KeyguardQuickAffordanceViewBinder,
) : BaseShortcutSection() {
// Amount to increase the bottom margin by to avoid colliding with inset
@@ -82,24 +77,18 @@
override fun bindData(constraintLayout: ConstraintLayout) {
if (KeyguardBottomAreaRefactor.isEnabled) {
leftShortcutHandle =
- KeyguardQuickAffordanceViewBinder.bind(
+ keyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.start_button),
keyguardQuickAffordancesCombinedViewModel.startButton,
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
- falsingManager,
- vibratorHelper,
- shortcutsLogger,
) {
indicationController.showTransientIndication(it)
}
rightShortcutHandle =
- KeyguardQuickAffordanceViewBinder.bind(
+ keyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.end_button),
keyguardQuickAffordancesCombinedViewModel.endButton,
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
- falsingManager,
- vibratorHelper,
- shortcutsLogger,
) {
indicationController.showTransientIndication(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
index c590f07..992550c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
@@ -47,6 +48,15 @@
edge = Edge.create(from = ALTERNATE_BOUNCER, to = AOD),
)
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var startAlpha = 1f
+ return transitionAnimation.sharedFlow(
+ duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 1f, it) },
+ )
+ }
+
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index df0b3dc..4908dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shared.recents.utilities.Utilities.clamp
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,6 +51,7 @@
private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
val alpha: Flow<Float> =
alternateBouncerViewModel.transitionToAlternateBouncerProgress.map {
+ SceneContainerFlag.assertInLegacyMode()
clamp(it * 2f, 0f, 1f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 470f17b..7b0b23f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -18,15 +18,20 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Color
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
@ExperimentalCoroutinesApi
class AlternateBouncerViewModel
@@ -34,12 +39,18 @@
constructor(
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
+ alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>,
) {
// When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
private val alternateBouncerScrimAlpha = .66f
+ /** Reports the alternate bouncer visible state if the scene container flag is enabled. */
+ val isVisible: Flow<Boolean> =
+ alternateBouncerInteractor.get().isVisible.onEach { SceneContainerFlag.assertInNewMode() }
+
/** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
- val transitionToAlternateBouncerProgress =
+ val transitionToAlternateBouncerProgress: Flow<Float> =
keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER)
/** An observable for the scrim alpha. */
@@ -51,11 +62,16 @@
val registerForDismissGestures: Flow<Boolean> =
transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged()
- fun showPrimaryBouncer() {
+ fun onTapped() {
statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
}
- fun hideAlternateBouncer() {
+ fun onRemovedFromWindow() {
statusBarKeyguardViewManager.hideAlternateBouncer(false)
}
+
+ fun onBackRequested() {
+ statusBarKeyguardViewManager.hideAlternateBouncer(false)
+ dismissCallbackRegistry.notifyDismissCancelled()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 680f966..2426f97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -19,6 +19,7 @@
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.FlowTracing.traceEmissionCount
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -29,19 +30,23 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.stateIn
@OptIn(ExperimentalCoroutinesApi::class)
class KeyguardQuickAffordancesCombinedViewModel
@Inject
constructor(
+ @Application private val applicationScope: CoroutineScope,
private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
private val keyguardInteractor: KeyguardInteractor,
shadeInteractor: ShadeInteractor,
@@ -133,9 +138,14 @@
/** The source of truth of alpha for all of the quick affordances on lockscreen */
val transitionAlpha: Flow<Float> =
merge(
- fadeInAlpha,
- fadeOutAlpha,
- )
+ fadeInAlpha,
+ fadeOutAlpha,
+ )
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = 0f,
+ )
/**
* Whether quick affordances are "opaque enough" to be considered visible to and interactive by
@@ -199,38 +209,42 @@
private fun button(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceViewModel> {
- return previewMode.flatMapLatest { previewMode ->
- combine(
- if (previewMode.isInPreviewMode) {
- quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position)
- } else {
- quickAffordanceInteractor.quickAffordance(position = position)
- },
- keyguardInteractor.animateDozingTransitions.distinctUntilChanged(),
- areQuickAffordancesFullyOpaque,
- selectedPreviewSlotId,
- quickAffordanceInteractor.useLongPress(),
- ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId, useLongPress ->
- val slotId = position.toSlotId()
- val isSelected = selectedPreviewSlotId == slotId
- model.toViewModel(
- animateReveal = !previewMode.isInPreviewMode && animateReveal,
- isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
- isSelected =
- previewMode.isInPreviewMode &&
- previewMode.shouldHighlightSelectedAffordance &&
- isSelected,
- isDimmed =
- previewMode.isInPreviewMode &&
- previewMode.shouldHighlightSelectedAffordance &&
- !isSelected,
- forceInactive = previewMode.isInPreviewMode,
- slotId = slotId,
- useLongPress = useLongPress,
- )
- }
- .distinctUntilChanged()
- }.traceEmissionCount({"QuickAfforcances#button${position.toSlotId()}"})
+ return previewMode
+ .flatMapLatest { previewMode ->
+ combine(
+ if (previewMode.isInPreviewMode) {
+ quickAffordanceInteractor.quickAffordanceAlwaysVisible(
+ position = position
+ )
+ } else {
+ quickAffordanceInteractor.quickAffordance(position = position)
+ },
+ keyguardInteractor.animateDozingTransitions.distinctUntilChanged(),
+ areQuickAffordancesFullyOpaque,
+ selectedPreviewSlotId,
+ quickAffordanceInteractor.useLongPress(),
+ ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId, useLongPress ->
+ val slotId = position.toSlotId()
+ val isSelected = selectedPreviewSlotId == slotId
+ model.toViewModel(
+ animateReveal = !previewMode.isInPreviewMode && animateReveal,
+ isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
+ isSelected =
+ previewMode.isInPreviewMode &&
+ previewMode.shouldHighlightSelectedAffordance &&
+ isSelected,
+ isDimmed =
+ previewMode.isInPreviewMode &&
+ previewMode.shouldHighlightSelectedAffordance &&
+ !isSelected,
+ forceInactive = previewMode.isInPreviewMode,
+ slotId = slotId,
+ useLongPress = useLongPress,
+ )
+ }
+ .distinctUntilChanged()
+ }
+ .traceEmissionCount({ "QuickAfforcances#button${position.toSlotId()}" })
}
private fun KeyguardQuickAffordanceModel.toViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 38a2b1b..050ef6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -83,6 +83,7 @@
private val communalInteractor: CommunalInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
+ private val alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
private val alternateBouncerToLockscreenTransitionViewModel:
@@ -239,6 +240,7 @@
merge(
alphaOnShadeExpansion,
keyguardInteractor.dismissAlpha,
+ alternateBouncerToAodTransitionViewModel.lockscreenAlpha(viewState),
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha(viewState),
alternateBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index 661da6d..c2b5d98 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -227,13 +227,33 @@
}
/**
+ * Runs the given [block] in a new coroutine when `this` [View]'s Window's [WindowLifecycleState] is
+ * at least at [state] (or immediately after calling this function if the window is already at least
+ * at [state]), automatically canceling the work when the window is no longer at least at that
+ * state.
+ *
+ * [block] may be run multiple times, running once per every time this` [View]'s Window's
+ * [WindowLifecycleState] becomes at least at [state].
+ */
+suspend fun View.repeatOnWindowLifecycle(
+ state: WindowLifecycleState,
+ block: suspend CoroutineScope.() -> Unit,
+): Nothing {
+ when (state) {
+ WindowLifecycleState.ATTACHED -> repeatWhenAttachedToWindow(block)
+ WindowLifecycleState.VISIBLE -> repeatWhenWindowIsVisible(block)
+ WindowLifecycleState.FOCUSED -> repeatWhenWindowHasFocus(block)
+ }
+}
+
+/**
* Runs the given [block] every time the [View] becomes attached (or immediately after calling this
* function, if the view was already attached), automatically canceling the work when the view
* becomes detached.
*
* Only use from the main thread.
*
- * The [block] may be run multiple times, running once per every time the view is attached.
+ * [block] may be run multiple times, running once per every time the view is attached.
*/
@MainThread
suspend fun View.repeatWhenAttachedToWindow(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -249,7 +269,7 @@
*
* Only use from the main thread.
*
- * The [block] may be run multiple times, running once per every time the window becomes visible.
+ * [block] may be run multiple times, running once per every time the window becomes visible.
*/
@MainThread
suspend fun View.repeatWhenWindowIsVisible(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -265,7 +285,7 @@
*
* Only use from the main thread.
*
- * The [block] may be run multiple times, running once per every time the window is focused.
+ * [block] may be run multiple times, running once per every time the window is focused.
*/
@MainThread
suspend fun View.repeatWhenWindowHasFocus(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -274,6 +294,21 @@
awaitCancellation() // satisfies return type of Nothing
}
+/** Lifecycle states for a [View]'s interaction with a [android.view.Window]. */
+enum class WindowLifecycleState {
+ /** Indicates that the [View] is attached to a [android.view.Window]. */
+ ATTACHED,
+ /**
+ * Indicates that the [View] is attached to a [android.view.Window], and the window is visible.
+ */
+ VISIBLE,
+ /**
+ * Indicates that the [View] is attached to a [android.view.Window], and the window is visible
+ * and focused.
+ */
+ FOCUSED
+}
+
private val View.isAttached
get() = conflatedCallbackFlow {
val onAttachListener =
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
index 0af5fea..7731481 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
@@ -16,9 +16,10 @@
package com.android.systemui.lifecycle
+import android.view.View
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.remember
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/** Base class for all System UI view-models. */
abstract class SysUiViewModel : SafeActivatable() {
@@ -37,8 +38,20 @@
fun <T : SysUiViewModel> rememberViewModel(
key: Any = Unit,
factory: () -> T,
-): T {
- val instance = remember(key) { factory() }
- LaunchedEffect(instance) { instance.activate() }
- return instance
-}
+): T = rememberActivated(key, factory)
+
+/**
+ * Invokes [block] in a new coroutine with a new [SysUiViewModel] that is automatically activated
+ * whenever `this` [View]'s Window's [WindowLifecycleState] is at least at
+ * [minWindowLifecycleState], and is automatically canceled once that is no longer the case.
+ */
+suspend fun <T : SysUiViewModel> View.viewModel(
+ minWindowLifecycleState: WindowLifecycleState,
+ factory: () -> T,
+ block: suspend CoroutineScope.(T) -> Unit,
+): Nothing =
+ repeatOnWindowLifecycle(minWindowLifecycleState) {
+ val instance = factory()
+ launch { instance.activate() }
+ block(instance)
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 46aa064..2ce7044 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -27,7 +27,6 @@
import android.window.RemoteTransition
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.Flags.pssAppSelectorAbruptExitFix
import com.android.systemui.Flags.pssAppSelectorRecentsSplitScreen
import com.android.systemui.display.naturalBounds
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
@@ -160,7 +159,7 @@
private fun createAnimation(task: RecentTask, view: View): ActivityOptions =
- if (pssAppSelectorAbruptExitFix() && task.isForegroundTask) {
+ if (task.isForegroundTask) {
// When the selected task is in the foreground, the scale up animation doesn't work.
// We fallback to the default close animation.
ActivityOptions.makeCustomTaskAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index e2ba761..a8b979e0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -16,11 +16,16 @@
package com.android.systemui.navigationbar;
+import static com.android.systemui.Flags.enableViewCaptureTracing;
+import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
+
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
@@ -28,6 +33,7 @@
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.res.R;
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -73,4 +79,15 @@
static WindowManager provideWindowManager(@DisplayId Context context) {
return context.getSystemService(WindowManager.class);
}
+
+ /** A ViewCaptureAwareWindowManager specific to the display's context. */
+ @Provides
+ @NavigationBarScope
+ @DisplayId
+ static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
+ @DisplayId WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) {
+ return new ViewCaptureAwareWindowManager(windowManager,
+ /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
+ /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index 7b248eb..e895d83 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -102,6 +102,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
@@ -196,6 +197,7 @@
private final Context mContext;
private final Bundle mSavedState;
private final WindowManager mWindowManager;
+ private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final AccessibilityManager mAccessibilityManager;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
@@ -556,6 +558,7 @@
@Nullable Bundle savedState,
@DisplayId Context context,
@DisplayId WindowManager windowManager,
+ @DisplayId ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
Lazy<AssistManager> assistManagerLazy,
AccessibilityManager accessibilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -601,6 +604,7 @@
mContext = context;
mSavedState = savedState;
mWindowManager = windowManager;
+ mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mAccessibilityManager = accessibilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
@@ -721,7 +725,7 @@
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView);
- mWindowManager.addView(mFrame,
+ mViewCaptureAwareWindowManager.addView(mFrame,
getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
.getRotation()));
mDisplayId = mContext.getDisplayId();
@@ -764,7 +768,7 @@
mCommandQueue.removeCallback(this);
Trace.beginSection("NavigationBar#removeViewImmediate");
try {
- mWindowManager.removeViewImmediate(mView.getRootView());
+ mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView());
} finally {
Trace.endSection();
}
@@ -866,7 +870,7 @@
if (mOrientationHandle != null) {
resetSecondaryHandle();
getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
- mWindowManager.removeView(mOrientationHandle);
+ mViewCaptureAwareWindowManager.removeView(mOrientationHandle);
mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
mOrientationHandleGlobalLayoutListener);
}
@@ -937,7 +941,7 @@
mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION
| WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
- mWindowManager.addView(mOrientationHandle, mOrientationParams);
+ mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams);
mOrientationHandle.setVisibility(View.GONE);
logNavbarOrientation("initSecondaryHomeHandleForRotation");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
index 1b34c33..89be17b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
@@ -19,6 +19,7 @@
import android.database.ContentObserver;
import android.os.Handler;
+import com.android.systemui.Flags;
import com.android.systemui.statusbar.policy.Listenable;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
@@ -76,10 +77,20 @@
mListening = listening;
if (listening) {
mObservedValue = getValueFromProvider();
- mSettingsProxy.registerContentObserverForUserSync(
- mSettingsProxy.getUriFor(mSettingName), false, this, mUserId);
+ if (Flags.qsRegisterSettingObserverOnBgThread()) {
+ mSettingsProxy.registerContentObserverForUserAsync(
+ mSettingsProxy.getUriFor(mSettingName), this, mUserId, () ->
+ mObservedValue = getValueFromProvider());
+ } else {
+ mSettingsProxy.registerContentObserverForUserSync(
+ mSettingsProxy.getUriFor(mSettingName), false, this, mUserId);
+ }
} else {
- mSettingsProxy.unregisterContentObserverSync(this);
+ if (Flags.qsRegisterSettingObserverOnBgThread()) {
+ mSettingsProxy.unregisterContentObserverAsync(this);
+ } else {
+ mSettingsProxy.unregisterContentObserverSync(this);
+ }
mObservedValue = mDefaultValue;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 98a61df..863a899 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -24,6 +24,7 @@
import android.net.Uri
import android.os.Handler
import android.os.UserHandle
+import android.util.Log
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.qualifiers.LongRunning
@@ -71,6 +72,7 @@
override fun provideRecordingServiceStrings(): RecordingServiceStrings = IrsStrings(resources)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Log.d(getTag(), "handling action: ${intent?.action}")
when (intent?.action) {
ACTION_START -> {
bgExecutor.execute {
@@ -95,7 +97,7 @@
bgExecutor.execute {
mNotificationManager.cancelAsUser(
null,
- mNotificationId,
+ intent.getIntExtra(EXTRA_NOTIFICATION_ID, mNotificationId),
UserHandle(mUserContextTracker.userContext.userId)
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/OWNERS b/packages/SystemUI/src/com/android/systemui/scene/OWNERS
index 033ff14..2ffcad4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/scene/OWNERS
@@ -1,2 +1,13 @@
+set noparent
+
+# Bug component: 1215786
+
justinweir@google.com
nijamkin@google.com
+
+# SysUI Dr No's.
+# Don't send reviews here.
+cinek@google.com
+dsandler@android.com
+juliacr@google.com
+pixel@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 5b50133..3ec088c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -25,6 +25,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
@@ -132,6 +133,7 @@
private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
private val dismissCallbackRegistry: DismissCallbackRegistry,
private val statusBarStateController: SysuiStatusBarStateController,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
@@ -152,6 +154,7 @@
handleKeyguardEnabledness()
notifyKeyguardDismissCallbacks()
refreshLockscreenEnabled()
+ handleHideAlternateBouncerOnTransitionToGone()
} else {
sceneLogger.logFrameworkEnabled(
isEnabled = false,
@@ -228,13 +231,16 @@
},
headsUpInteractor.isHeadsUpOrAnimatingAway,
occlusionInteractor.invisibleDueToOcclusion,
+ alternateBouncerInteractor.isVisible,
) {
visibilityForTransitionState,
isHeadsUpOrAnimatingAway,
invisibleDueToOcclusion,
+ isAlternateBouncerVisible,
->
when {
isHeadsUpOrAnimatingAway -> true to "showing a HUN"
+ isAlternateBouncerVisible -> true to "showing alternate bouncer"
invisibleDueToOcclusion -> false to "invisible due to occlusion"
else -> visibilityForTransitionState
}
@@ -351,7 +357,9 @@
)
}
val isOnLockscreen = renderedScenes.contains(Scenes.Lockscreen)
- val isOnBouncer = renderedScenes.contains(Scenes.Bouncer)
+ val isOnBouncer =
+ renderedScenes.contains(Scenes.Bouncer) ||
+ alternateBouncerInteractor.isVisibleState()
if (!deviceUnlockStatus.isUnlocked) {
return@mapNotNull if (isOnLockscreen || isOnBouncer) {
// Already on lockscreen or bouncer, no need to change scenes.
@@ -379,12 +387,13 @@
!statusBarStateController.leaveOpenOnKeyguardHide()
) {
Scenes.Gone to
- "device was unlocked in Bouncer scene and shade" +
+ "device was unlocked with bouncer showing and shade" +
" didn't need to be left open"
} else {
val prevScene = previousScene.value
(prevScene ?: Scenes.Gone) to
- "device was unlocked in Bouncer scene, from sceneKey=$prevScene"
+ "device was unlocked with bouncer showing," +
+ " from sceneKey=$prevScene"
}
isOnLockscreen ->
// The lockscreen should be dismissed automatically in 2 scenarios:
@@ -776,4 +785,14 @@
.collectLatest { deviceEntryInteractor.refreshLockscreenEnabled() }
}
}
+
+ private fun handleHideAlternateBouncerOnTransitionToGone() {
+ applicationScope.launch {
+ sceneInteractor.transitionState
+ .map { it.isIdle(Scenes.Gone) }
+ .distinctUntilChanged()
+ .filter { it }
+ .collectLatest { alternateBouncerInteractor.hide() }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index bccbb11..f6924f2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -5,15 +5,18 @@
import android.view.MotionEvent
import android.view.View
import android.view.WindowInsets
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.shade.TouchLogger
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
/** A root view of the main SysUI window that supports scenes. */
+@ExperimentalCoroutinesApi
class SceneWindowRootView(
context: Context,
attrs: AttributeSet?,
@@ -35,6 +38,7 @@
scenes: Set<Scene>,
layoutInsetController: LayoutInsetsController,
sceneDataSourceDelegator: SceneDataSourceDelegator,
+ alternateBouncerDependencies: AlternateBouncerDependencies,
) {
this.viewModel = viewModel
setLayoutInsetsController(layoutInsetController)
@@ -49,6 +53,7 @@
super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE)
},
dataSourceDelegator = sceneDataSourceDelegator,
+ alternateBouncerDependencies = alternateBouncerDependencies,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index d31d6f4..73a8e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -37,6 +37,8 @@
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
+import com.android.systemui.keyguard.ui.composable.AlternateBouncer
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -48,12 +50,14 @@
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+@ExperimentalCoroutinesApi
object SceneWindowRootViewBinder {
/** Binds between the view and view-model pertaining to a specific scene container. */
@@ -66,6 +70,7 @@
scenes: Set<Scene>,
onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
dataSourceDelegator: SceneDataSourceDelegator,
+ alternateBouncerDependencies: AlternateBouncerDependencies,
) {
val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
val sortedSceneByKey: Map<SceneKey, Scene> = buildMap {
@@ -120,6 +125,14 @@
sharedNotificationContainer
)
view.addView(sharedNotificationContainer)
+
+ // TODO (b/358354906): use an overlay for the alternate bouncer
+ view.addView(
+ createAlternateBouncerView(
+ context = view.context,
+ alternateBouncerDependencies = alternateBouncerDependencies,
+ )
+ )
}
launch {
@@ -164,6 +177,19 @@
}
}
+ private fun createAlternateBouncerView(
+ context: Context,
+ alternateBouncerDependencies: AlternateBouncerDependencies,
+ ): ComposeView {
+ return ComposeView(context).apply {
+ setContent {
+ AlternateBouncer(
+ alternateBouncerDependencies = alternateBouncerDependencies,
+ )
+ }
+ }
+ }
+
// TODO(b/298525212): remove once Compose exposes window inset bounds.
private fun displayCutoutFromWindowInsets(
scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 1170354..700253ba 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -63,7 +63,9 @@
protected static final int NOTIF_BASE_ID = 4273;
private static final String TAG = "RecordingService";
private static final String CHANNEL_ID = "screen_record";
- private static final String GROUP_KEY = "screen_record_saved";
+ @VisibleForTesting static final String GROUP_KEY_SAVED = "screen_record_saved";
+ private static final String GROUP_KEY_ERROR_STARTING = "screen_record_error_starting";
+ @VisibleForTesting static final String GROUP_KEY_ERROR_SAVING = "screen_record_error_saving";
private static final String EXTRA_RESULT_CODE = "extra_resultCode";
protected static final String EXTRA_PATH = "extra_path";
private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
@@ -78,6 +80,7 @@
"com.android.systemui.screenrecord.STOP_FROM_NOTIF";
protected static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+ protected static final String EXTRA_NOTIFICATION_ID = "notification_id";
private final RecordingController mController;
protected final KeyguardDismissUtil mKeyguardDismissUtil;
@@ -181,7 +184,7 @@
mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START);
} else {
updateState(false);
- createErrorStartingNotification();
+ createErrorStartingNotification(currentUser);
stopForeground(STOP_FOREGROUND_DETACH);
stopSelf();
return Service.START_NOT_STICKY;
@@ -276,8 +279,8 @@
* errors.
*/
@VisibleForTesting
- protected void createErrorStartingNotification() {
- createErrorNotification(strings().getStartError());
+ protected void createErrorStartingNotification(UserHandle currentUser) {
+ createErrorNotification(currentUser, strings().getStartError(), GROUP_KEY_ERROR_STARTING);
}
/**
@@ -285,17 +288,22 @@
* errors.
*/
@VisibleForTesting
- protected void createErrorSavingNotification() {
- createErrorNotification(strings().getSaveError());
+ protected void createErrorSavingNotification(UserHandle currentUser) {
+ createErrorNotification(currentUser, strings().getSaveError(), GROUP_KEY_ERROR_SAVING);
}
- private void createErrorNotification(String notificationContentTitle) {
+ private void createErrorNotification(
+ UserHandle currentUser, String notificationContentTitle, String groupKey) {
+ // Make sure error notifications get their own group.
+ postGroupSummaryNotification(currentUser, notificationContentTitle, groupKey);
+
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, strings().getTitle());
Notification.Builder builder = new Notification.Builder(this, getChannelId())
.setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(notificationContentTitle)
+ .setGroup(groupKey)
.addExtras(extras);
startForeground(mNotificationId, builder.build());
}
@@ -350,7 +358,7 @@
.setContentText(
strings().getBackgroundProcessingLabel())
.setSmallIcon(R.drawable.ic_screenrecord)
- .setGroup(GROUP_KEY)
+ .setGroup(GROUP_KEY_SAVED)
.addExtras(extras);
return builder.build();
}
@@ -387,7 +395,7 @@
PendingIntent.FLAG_IMMUTABLE))
.addAction(shareAction)
.setAutoCancel(true)
- .setGroup(GROUP_KEY)
+ .setGroup(GROUP_KEY_SAVED)
.addExtras(extras);
// Add thumbnail if available
@@ -402,21 +410,28 @@
}
/**
- * Adds a group notification so that save notifications from multiple recordings are
- * grouped together, and the foreground service recording notification is not
+ * Adds a group summary notification for save notifications so that save notifications from
+ * multiple recordings are grouped together, and the foreground service recording notification
+ * is not.
*/
- private void postGroupNotification(UserHandle currentUser) {
+ private void postGroupSummaryNotificationForSaves(UserHandle currentUser) {
+ postGroupSummaryNotification(currentUser, strings().getSaveTitle(), GROUP_KEY_SAVED);
+ }
+
+ /** Posts a group summary notification for the given group. */
+ private void postGroupSummaryNotification(
+ UserHandle currentUser, String notificationContentTitle, String groupKey) {
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
strings().getTitle());
Notification groupNotif = new Notification.Builder(this, getChannelId())
.setSmallIcon(R.drawable.ic_screenrecord)
- .setContentTitle(strings().getSaveTitle())
- .setGroup(GROUP_KEY)
+ .setContentTitle(notificationContentTitle)
+ .setGroup(groupKey)
.setGroupSummary(true)
.setExtras(extras)
.build();
- mNotificationManager.notifyAsUser(getTag(), NOTIF_BASE_ID, groupNotif, currentUser);
+ mNotificationManager.notifyAsUser(getTag(), mNotificationId, groupNotif, currentUser);
}
private void stopService() {
@@ -427,6 +442,7 @@
if (userId == USER_ID_NOT_SPECIFIED) {
userId = mUserContextTracker.getUserContext().getUserId();
}
+ UserHandle currentUser = new UserHandle(userId);
Log.d(getTag(), "notifying for user " + userId);
setTapsVisible(mOriginalShowTaps);
try {
@@ -444,7 +460,7 @@
Log.e(getTag(), "stopRecording called, but there was an error when ending"
+ "recording");
exception.printStackTrace();
- createErrorSavingNotification();
+ createErrorSavingNotification(currentUser);
} catch (Throwable throwable) {
if (getRecorder() != null) {
// Something unexpected happen, SystemUI will crash but let's delete
@@ -468,7 +484,7 @@
Log.d(getTag(), "saving recording");
Notification notification = createSaveNotification(
getRecorder() != null ? getRecorder().save() : null);
- postGroupNotification(currentUser);
+ postGroupSummaryNotificationForSaves(currentUser);
mNotificationManager.notifyAsUser(null, mNotificationId, notification,
currentUser);
} catch (IOException | IllegalStateException e) {
@@ -527,7 +543,8 @@
private Intent getShareIntent(Context context, Uri path) {
return new Intent(context, this.getClass()).setAction(ACTION_SHARE)
- .putExtra(EXTRA_PATH, path);
+ .putExtra(EXTRA_PATH, path)
+ .putExtra(EXTRA_NOTIFICATION_ID, mNotificationId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 0a092a0..16aef65 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -2252,8 +2252,11 @@
// panel, mQs will not need to be null cause it will be tied to the same lifecycle.
if (fragment == mQs) {
// Clear it to remove bindings to mQs from the provider.
- mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(null);
- mNotificationStackScrollLayoutController.setQsHeader(null);
+ if (QSComposeFragment.isEnabled()) {
+ mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(null);
+ } else {
+ mNotificationStackScrollLayoutController.setQsHeader(null);
+ }
mQs = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index bc23778..21bbaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -31,6 +31,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -83,6 +84,7 @@
scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
layoutInsetController: NotificationInsetsController,
sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
+ alternateBouncerDependencies: Provider<AlternateBouncerDependencies>,
): WindowRootView {
return if (SceneContainerFlag.isEnabled) {
checkNoSceneDuplicates(scenesProvider.get())
@@ -96,6 +98,7 @@
scenes = scenesProvider.get(),
layoutInsetController = layoutInsetController,
sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
+ alternateBouncerDependencies = alternateBouncerDependencies.get(),
)
sceneWindowRootView
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
index 9e221d3..f48e31e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import javax.inject.Inject
@@ -46,6 +47,10 @@
sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
repository: ShadeRepository,
) : BaseShadeInteractor {
+ init {
+ SceneContainerFlag.assertInLegacyMode()
+ }
+
/**
* The amount [0-1] that the shade has been opened. Uses stateIn to avoid redundant calculations
* in downstream flows.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 9617b54..6a21531 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -22,8 +22,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -43,8 +44,12 @@
constructor(
@Application scope: CoroutineScope,
sceneInteractor: SceneInteractor,
- sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
+ shadeRepository: ShadeRepository,
) : BaseShadeInteractor {
+ init {
+ SceneContainerFlag.assertInNewMode()
+ }
+
override val shadeExpansion: StateFlow<Float> =
sceneBasedExpansion(sceneInteractor, SceneFamilies.NotifShade)
.traceAsCounter("panel_expansion") { (it * 100f).toInt() }
@@ -55,7 +60,7 @@
override val qsExpansion: StateFlow<Float> =
combine(
- sharedNotificationContainerInteractor.isSplitShadeEnabled,
+ shadeRepository.isShadeLayoutWide,
shadeExpansion,
sceneBasedQsExpansion,
) { isSplitShadeEnabled, shadeExpansion, qsExpansion ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 1027bc9..9b382e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -89,6 +89,7 @@
.filter { it.callType == CallType.Ongoing }
.minByOrNull { it.whenTime }
}
+ .distinctUntilChanged()
.flowOn(backgroundDispatcher)
/** Are any notifications being actively presented in the notification stack? */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 2537aff..5d37476 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -23,7 +23,9 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.policy.SplitShadeStateController
import dagger.Lazy
import javax.inject.Inject
@@ -43,7 +45,8 @@
constructor(
configurationRepository: ConfigurationRepository,
private val context: Context,
- private val splitShadeStateController: SplitShadeStateController,
+ private val splitShadeStateController: Lazy<SplitShadeStateController>,
+ private val shadeInteractor: Lazy<ShadeInteractor>,
keyguardInteractor: KeyguardInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
@@ -56,16 +59,33 @@
/** An internal modification was made to notifications */
val notificationStackChanged = _notificationStackChanged.debounce(20L)
+ private val configurationChangeEvents =
+ configurationRepository.onAnyConfigurationChange.onStart { emit(Unit) }
+
+ /* Warning: Even though the value it emits only contains the split shade status, this flow must
+ * emit a value whenever the configuration *or* the split shade status changes. Adding a
+ * distinctUntilChanged() to this would cause configurationBasedDimensions to miss configuration
+ * updates that affect other resources, like margins or the large screen header flag.
+ */
+ private val dimensionsUpdateEventsWithShouldUseSplitShade: Flow<Boolean> =
+ if (SceneContainerFlag.isEnabled) {
+ combine(configurationChangeEvents, shadeInteractor.get().isShadeLayoutWide) {
+ _,
+ isShadeLayoutWide ->
+ isShadeLayoutWide
+ }
+ } else {
+ configurationChangeEvents.map {
+ splitShadeStateController.get().shouldUseSplitNotificationShade(context.resources)
+ }
+ }
+
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
- configurationRepository.onAnyConfigurationChange
- .onStart { emit(Unit) }
- .map { _ ->
+ dimensionsUpdateEventsWithShouldUseSplitShade
+ .map { shouldUseSplitShade ->
with(context.resources) {
ConfigurationBasedDimensions(
- useSplitShade =
- splitShadeStateController.shouldUseSplitNotificationShade(
- context.resources
- ),
+ useSplitShade = shouldUseSplitShade,
useLargeScreenHeader =
getBoolean(R.bool.config_use_large_screen_shade_header),
marginHorizontal =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index fd08e89..a30b877 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import android.util.Log
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationScrollViewModel
@@ -33,7 +33,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
@@ -46,7 +45,7 @@
dumpManager: DumpManager,
@Main private val mainImmediateDispatcher: CoroutineDispatcher,
private val view: NotificationScrollView,
- private val viewModel: NotificationScrollViewModel,
+ private val viewModelFactory: NotificationScrollViewModel.Factory,
private val configuration: ConfigurationState,
) : FlowDumperImpl(dumpManager) {
@@ -61,38 +60,42 @@
}
fun bindWhileAttached(): DisposableHandle {
- return view.asView().repeatWhenAttached(mainImmediateDispatcher) {
- repeatOnLifecycle(Lifecycle.State.CREATED) { bind() }
- }
+ return view.asView().repeatWhenAttached(mainImmediateDispatcher) { bind() }
}
- suspend fun bind() = coroutineScope {
- launchAndDispose {
- updateViewPosition()
- view.asView().onLayoutChanged { updateViewPosition() }
- }
+ suspend fun bind(): Nothing =
+ view.asView().viewModel(
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = viewModelFactory::create,
+ ) { viewModel ->
+ launchAndDispose {
+ updateViewPosition()
+ view.asView().onLayoutChanged { updateViewPosition() }
+ }
- launch {
- viewModel
- .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
- .collect { view.setScrimClippingShape(it) }
- }
+ launch {
+ viewModel
+ .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
+ .collect { view.setScrimClippingShape(it) }
+ }
- launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
- launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
- launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
- launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
- launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
+ launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
+ launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
+ launch {
+ viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) }
+ }
+ launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
+ launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
- launchAndDispose {
- view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
- view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
- DisposableHandle {
- view.setSyntheticScrollConsumer(null)
- view.setCurrentGestureOverscrollConsumer(null)
+ launchAndDispose {
+ view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
+ view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
+ DisposableHandle {
+ view.setSyntheticScrollConsumer(null)
+ view.setCurrentGestureOverscrollConsumer(null)
+ }
}
}
- }
/** flow of the scrim clipping radius */
private val scrimRadius: Flow<Int>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 0541550..aa1911e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -99,19 +99,20 @@
disposables +=
view.repeatWhenAttached(mainImmediateDispatcher) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch {
- // Only temporarily needed, until flexi notifs go live
- viewModel.shadeCollapseFadeIn.collect { fadeIn ->
- if (fadeIn) {
- android.animation.ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 250
- addUpdateListener { animation ->
- controller.setMaxAlphaForKeyguard(
- animation.animatedFraction,
- "SharedNotificationContainerVB (collapseFadeIn)"
- )
+ if (!SceneContainerFlag.isEnabled) {
+ launch {
+ viewModel.shadeCollapseFadeIn.collect { fadeIn ->
+ if (fadeIn) {
+ android.animation.ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = 250
+ addUpdateListener { animation ->
+ controller.setMaxAlphaForKeyguard(
+ animation.animatedFraction,
+ "SharedNotificationContainerVB (collapseFadeIn)"
+ )
+ }
+ start()
}
- start()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 2ba79a8..bfb624a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -19,9 +19,9 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.lifecycle.SysUiViewModel
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.SceneFamilies
@@ -33,9 +33,11 @@
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
-import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.kotlin.ActivatableFlowDumper
+import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl
import dagger.Lazy
-import javax.inject.Inject
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -43,9 +45,8 @@
import kotlinx.coroutines.flow.map
/** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
-@SysUISingleton
class NotificationScrollViewModel
-@Inject
+@AssistedInject
constructor(
dumpManager: DumpManager,
stackAppearanceInteractor: NotificationStackAppearanceInteractor,
@@ -54,7 +55,14 @@
// TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
// while the flag is off, creating this object too early results in a crash
keyguardInteractor: Lazy<KeyguardInteractor>,
-) : FlowDumperImpl(dumpManager) {
+) :
+ ActivatableFlowDumper by ActivatableFlowDumperImpl(dumpManager, "NotificationScrollViewModel"),
+ SysUiViewModel() {
+
+ override suspend fun onActivated() {
+ activateFlowDumper()
+ }
+
/**
* The expansion fraction of the notification stack. It should go from 0 to 1 when transitioning
* from Gone to Shade scenes, and remain at 1 when in Lockscreen or Shade scenes and while
@@ -186,4 +194,9 @@
keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing")
}
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): NotificationScrollViewModel
+ }
}
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 b6d58d6..ada3c52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2353,7 +2353,11 @@
&& !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
&& mStatusBarKeyguardViewManager.isSecure()) {
Log.d(TAG, "showBouncerOrLockScreenIfKeyguard, showingBouncer");
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+ if (SceneContainerFlag.isEnabled()) {
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ } else {
+ mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+ }
}
}
}
@@ -2985,7 +2989,7 @@
@Override
public void onFalse() {
// Hides quick settings, bouncer, and quick-quick settings.
- mStatusBarKeyguardViewManager.reset(true);
+ mStatusBarKeyguardViewManager.reset(true, /* isFalsingReset= */true);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 2d775b7..f8f9b77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -708,7 +708,7 @@
* Shows the notification keyguard or the bouncer depending on
* {@link #needsFullscreenBouncer()}.
*/
- protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
+ protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset) {
boolean isDozing = mDozing;
if (Flags.simPinRaceConditionOnRestart()) {
KeyguardState toState = mKeyguardTransitionInteractor.getTransitionState().getValue()
@@ -734,8 +734,11 @@
mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
}
}
- } else {
- Log.e(TAG, "Attempted to show the sim bouncer when it is already showing.");
+ } else if (!isFalsingReset) {
+ // Falsing resets can cause this to flicker, so don't reset in this case
+ Log.i(TAG, "Sim bouncer is already showing, issuing a refresh");
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
+
}
} else {
mCentralSurfaces.showKeyguard();
@@ -957,6 +960,10 @@
@Override
public void reset(boolean hideBouncerWhenShowing) {
+ reset(hideBouncerWhenShowing, /* isFalsingReset= */false);
+ }
+
+ public void reset(boolean hideBouncerWhenShowing, boolean isFalsingReset) {
if (mKeyguardStateController.isShowing() && !bouncerIsAnimatingAway()) {
final boolean isOccluded = mKeyguardStateController.isOccluded();
// Hide quick settings.
@@ -968,7 +975,7 @@
hideBouncer(false /* destroyView */);
}
} else {
- showBouncerOrKeyguard(hideBouncerWhenShowing);
+ showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset);
}
if (hideBouncerWhenShowing) {
hideAlternateBouncer(true);
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/ActionKeyTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/ActionKeyTutorialScreen.kt
new file mode 100644
index 0000000..f7f2631
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/ActionKeyTutorialScreen.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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.touchpad.tutorial.ui.composable
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.type
+import com.airbnb.lottie.compose.rememberLottieDynamicProperties
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.res.R
+import com.android.systemui.touchpad.tutorial.ui.composable.TutorialActionState.FINISHED
+import com.android.systemui.touchpad.tutorial.ui.composable.TutorialActionState.NOT_STARTED
+
+@Composable
+fun ActionKeyTutorialScreen(
+ onDoneButtonClicked: () -> Unit,
+ onBack: () -> Unit,
+) {
+ BackHandler(onBack = onBack)
+ val screenConfig = buildScreenConfig()
+ var actionState by remember { mutableStateOf(NOT_STARTED) }
+ Box(
+ modifier =
+ Modifier.fillMaxSize().onKeyEvent { keyEvent: KeyEvent ->
+ // temporary before we can access Action/Meta key
+ if (keyEvent.key == Key.AltLeft && keyEvent.type == KeyEventType.KeyUp) {
+ actionState = FINISHED
+ }
+ true
+ }
+ ) {
+ ActionTutorialContent(actionState, onDoneButtonClicked, screenConfig)
+ }
+}
+
+@Composable
+private fun buildScreenConfig() =
+ TutorialScreenConfig(
+ colors = rememberScreenColors(),
+ strings =
+ TutorialScreenConfig.Strings(
+ titleResId = R.string.tutorial_action_key_title,
+ bodyResId = R.string.tutorial_action_key_guidance,
+ titleSuccessResId = R.string.tutorial_action_key_success_title,
+ bodySuccessResId = R.string.tutorial_action_key_success_body
+ ),
+ animations =
+ TutorialScreenConfig.Animations(
+ educationResId = R.raw.action_key_edu,
+ successResId = R.raw.action_key_success
+ )
+ )
+
+@Composable
+private fun rememberScreenColors(): TutorialScreenConfig.Colors {
+ val primaryFixedDim = LocalAndroidColorScheme.current.primaryFixedDim
+ val secondaryFixedDim = LocalAndroidColorScheme.current.secondaryFixedDim
+ val onSecondaryFixed = LocalAndroidColorScheme.current.onSecondaryFixed
+ val onSecondaryFixedVariant = LocalAndroidColorScheme.current.onSecondaryFixedVariant
+ val surfaceContainer = MaterialTheme.colorScheme.surfaceContainer
+ val dynamicProperties =
+ rememberLottieDynamicProperties(
+ rememberColorFilterProperty(".primaryFixedDim", primaryFixedDim),
+ rememberColorFilterProperty(".secondaryFixedDim", secondaryFixedDim),
+ rememberColorFilterProperty(".onSecondaryFixed", onSecondaryFixed),
+ rememberColorFilterProperty(".onSecondaryFixedVariant", onSecondaryFixedVariant)
+ )
+ val screenColors =
+ remember(surfaceContainer, dynamicProperties) {
+ TutorialScreenConfig.Colors(
+ background = onSecondaryFixed,
+ successBackground = surfaceContainer,
+ title = primaryFixedDim,
+ animationColors = dynamicProperties,
+ )
+ }
+ return screenColors
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/ActionTutorialContent.kt
new file mode 100644
index 0000000..2b7f674
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/ActionTutorialContent.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 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.touchpad.tutorial.ui.composable
+
+import android.graphics.ColorFilter
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import androidx.annotation.RawRes
+import androidx.annotation.StringRes
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.LinearEasing
+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.togetherWith
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.LottieDynamicProperties
+import com.airbnb.lottie.compose.LottieDynamicProperty
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+import com.airbnb.lottie.compose.rememberLottieDynamicProperty
+import com.android.systemui.touchpad.tutorial.ui.composable.TutorialActionState.FINISHED
+import com.android.systemui.touchpad.tutorial.ui.composable.TutorialActionState.IN_PROGRESS
+import com.android.systemui.touchpad.tutorial.ui.composable.TutorialActionState.NOT_STARTED
+
+enum class TutorialActionState {
+ NOT_STARTED,
+ IN_PROGRESS,
+ FINISHED
+}
+
+@Composable
+fun ActionTutorialContent(
+ actionState: TutorialActionState,
+ onDoneButtonClicked: () -> Unit,
+ config: TutorialScreenConfig
+) {
+ val animatedColor by
+ animateColorAsState(
+ targetValue =
+ if (actionState == FINISHED) config.colors.successBackground
+ else config.colors.background,
+ animationSpec = tween(durationMillis = 150, easing = LinearEasing),
+ label = "backgroundColor"
+ )
+ Column(
+ verticalArrangement = Arrangement.Center,
+ modifier =
+ Modifier.fillMaxSize()
+ .drawBehind { drawRect(animatedColor) }
+ .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
+ ) {
+ Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
+ TutorialDescription(
+ titleTextId =
+ if (actionState == FINISHED) config.strings.titleSuccessResId
+ else config.strings.titleResId,
+ titleColor = config.colors.title,
+ bodyTextId =
+ if (actionState == FINISHED) config.strings.bodySuccessResId
+ else config.strings.bodyResId,
+ modifier = Modifier.weight(1f)
+ )
+ Spacer(modifier = Modifier.width(76.dp))
+ TutorialAnimation(
+ actionState,
+ config,
+ modifier = Modifier.weight(1f).padding(top = 8.dp)
+ )
+ }
+ DoneButton(onDoneButtonClicked = onDoneButtonClicked)
+ }
+}
+
+@Composable
+fun TutorialDescription(
+ @StringRes titleTextId: Int,
+ titleColor: Color,
+ @StringRes bodyTextId: Int,
+ modifier: Modifier = Modifier
+) {
+ Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
+ Text(
+ text = stringResource(id = titleTextId),
+ style = MaterialTheme.typography.displayLarge,
+ color = titleColor
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = stringResource(id = bodyTextId),
+ style = MaterialTheme.typography.bodyLarge,
+ color = Color.White
+ )
+ }
+}
+
+@Composable
+fun TutorialAnimation(
+ actionState: TutorialActionState,
+ config: TutorialScreenConfig,
+ modifier: Modifier = Modifier
+) {
+ Box(modifier = modifier.fillMaxWidth()) {
+ AnimatedContent(
+ targetState = actionState,
+ transitionSpec = {
+ if (initialState == NOT_STARTED) {
+ val transitionDurationMillis = 150
+ fadeIn(animationSpec = tween(transitionDurationMillis, easing = LinearEasing))
+ .togetherWith(
+ fadeOut(animationSpec = snap(delayMillis = transitionDurationMillis))
+ )
+ // we explicitly don't want size transform because when targetState
+ // animation is loaded for the first time, AnimatedContent thinks target
+ // size is smaller and tries to shrink initial state animation
+ .using(sizeTransform = null)
+ } else {
+ // empty transition works because all remaining transitions are from IN_PROGRESS
+ // state which shares initial animation frame with both FINISHED and NOT_STARTED
+ EnterTransition.None togetherWith ExitTransition.None
+ }
+ }
+ ) { state ->
+ when (state) {
+ NOT_STARTED ->
+ EducationAnimation(
+ config.animations.educationResId,
+ config.colors.animationColors
+ )
+ IN_PROGRESS ->
+ FrozenSuccessAnimation(
+ config.animations.successResId,
+ config.colors.animationColors
+ )
+ FINISHED ->
+ SuccessAnimation(config.animations.successResId, config.colors.animationColors)
+ }
+ }
+ }
+}
+
+@Composable
+private fun FrozenSuccessAnimation(
+ @RawRes successAnimationId: Int,
+ animationProperties: LottieDynamicProperties
+) {
+ val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
+ LottieAnimation(
+ composition = composition,
+ progress = { 0f }, // animation should freeze on 1st frame
+ dynamicProperties = animationProperties,
+ )
+}
+
+@Composable
+private fun EducationAnimation(
+ @RawRes educationAnimationId: Int,
+ animationProperties: LottieDynamicProperties
+) {
+ val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
+ val progress by
+ animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
+ LottieAnimation(
+ composition = composition,
+ progress = { progress },
+ dynamicProperties = animationProperties,
+ )
+}
+
+@Composable
+private fun SuccessAnimation(
+ @RawRes successAnimationId: Int,
+ animationProperties: LottieDynamicProperties
+) {
+ val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
+ val progress by animateLottieCompositionAsState(composition, iterations = 1)
+ LottieAnimation(
+ composition = composition,
+ progress = { progress },
+ dynamicProperties = animationProperties,
+ )
+}
+
+@Composable
+fun rememberColorFilterProperty(
+ layerName: String,
+ color: Color
+): LottieDynamicProperty<ColorFilter> {
+ return rememberLottieDynamicProperty(
+ LottieProperty.COLOR_FILTER,
+ value = PorterDuffColorFilter(color.toArgb(), PorterDuff.Mode.SRC_ATOP),
+ // "**" below means match zero or more layers, so ** layerName ** means find layer with that
+ // name at any depth
+ keyPath = arrayOf("**", layerName, "**")
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
index 9ac2cba..dfe9c0d 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
@@ -16,57 +16,18 @@
package com.android.systemui.touchpad.tutorial.ui.composable
-import android.graphics.ColorFilter
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
import androidx.activity.compose.BackHandler
-import androidx.annotation.RawRes
-import androidx.annotation.StringRes
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.core.LinearEasing
-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.togetherWith
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import com.airbnb.lottie.LottieProperty
-import com.airbnb.lottie.compose.LottieAnimation
-import com.airbnb.lottie.compose.LottieCompositionSpec
-import com.airbnb.lottie.compose.LottieConstants
-import com.airbnb.lottie.compose.LottieDynamicProperties
-import com.airbnb.lottie.compose.LottieDynamicProperty
-import com.airbnb.lottie.compose.animateLottieCompositionAsState
-import com.airbnb.lottie.compose.rememberLottieComposition
-import com.airbnb.lottie.compose.rememberLottieDynamicProperty
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
@@ -81,6 +42,14 @@
): TouchpadGestureMonitor
}
+fun GestureState.toTutorialActionState(): TutorialActionState {
+ return when (this) {
+ NOT_STARTED -> TutorialActionState.NOT_STARTED
+ IN_PROGRESS -> TutorialActionState.IN_PROGRESS
+ FINISHED -> TutorialActionState.FINISHED
+ }
+}
+
@Composable
fun GestureTutorialScreen(
screenConfig: TutorialScreenConfig,
@@ -104,7 +73,11 @@
)
}
TouchpadGesturesHandlingBox(gestureHandler, gestureState) {
- GestureTutorialContent(gestureState, onDoneButtonClicked, screenConfig)
+ ActionTutorialContent(
+ gestureState.toTutorialActionState(),
+ onDoneButtonClicked,
+ screenConfig
+ )
}
}
@@ -135,169 +108,3 @@
content()
}
}
-
-@Composable
-private fun GestureTutorialContent(
- gestureState: GestureState,
- onDoneButtonClicked: () -> Unit,
- config: TutorialScreenConfig
-) {
- val animatedColor by
- animateColorAsState(
- targetValue =
- if (gestureState == FINISHED) config.colors.successBackground
- else config.colors.background,
- animationSpec = tween(durationMillis = 150, easing = LinearEasing),
- label = "backgroundColor"
- )
- Column(
- verticalArrangement = Arrangement.Center,
- modifier =
- Modifier.fillMaxSize()
- .drawBehind { drawRect(animatedColor) }
- .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
- ) {
- Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
- TutorialDescription(
- titleTextId =
- if (gestureState == FINISHED) config.strings.titleSuccessResId
- else config.strings.titleResId,
- titleColor = config.colors.title,
- bodyTextId =
- if (gestureState == FINISHED) config.strings.bodySuccessResId
- else config.strings.bodyResId,
- modifier = Modifier.weight(1f)
- )
- Spacer(modifier = Modifier.width(76.dp))
- TutorialAnimation(
- gestureState,
- config,
- modifier = Modifier.weight(1f).padding(top = 8.dp)
- )
- }
- DoneButton(onDoneButtonClicked = onDoneButtonClicked)
- }
-}
-
-@Composable
-fun TutorialDescription(
- @StringRes titleTextId: Int,
- titleColor: Color,
- @StringRes bodyTextId: Int,
- modifier: Modifier = Modifier
-) {
- Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
- Text(
- text = stringResource(id = titleTextId),
- style = MaterialTheme.typography.displayLarge,
- color = titleColor
- )
- Spacer(modifier = Modifier.height(16.dp))
- Text(
- text = stringResource(id = bodyTextId),
- style = MaterialTheme.typography.bodyLarge,
- color = Color.White
- )
- }
-}
-
-@Composable
-fun TutorialAnimation(
- gestureState: GestureState,
- config: TutorialScreenConfig,
- modifier: Modifier = Modifier
-) {
- Box(modifier = modifier.fillMaxWidth()) {
- AnimatedContent(
- targetState = gestureState,
- transitionSpec = {
- if (initialState == NOT_STARTED && targetState == IN_PROGRESS) {
- val transitionDurationMillis = 150
- fadeIn(animationSpec = tween(transitionDurationMillis, easing = LinearEasing))
- .togetherWith(
- fadeOut(animationSpec = snap(delayMillis = transitionDurationMillis))
- )
- // we explicitly don't want size transform because when targetState
- // animation is loaded for the first time, AnimatedContent thinks target
- // size is smaller and tries to shrink initial state animation
- .using(sizeTransform = null)
- } else {
- // empty transition works because all remaining transitions are from IN_PROGRESS
- // state which shares initial animation frame with both FINISHED and NOT_STARTED
- EnterTransition.None togetherWith ExitTransition.None
- }
- }
- ) { gestureState ->
- when (gestureState) {
- NOT_STARTED ->
- EducationAnimation(
- config.animations.educationResId,
- config.colors.animationColors
- )
- IN_PROGRESS ->
- FrozenSuccessAnimation(
- config.animations.successResId,
- config.colors.animationColors
- )
- FINISHED ->
- SuccessAnimation(config.animations.successResId, config.colors.animationColors)
- }
- }
- }
-}
-
-@Composable
-private fun FrozenSuccessAnimation(
- @RawRes successAnimationId: Int,
- animationProperties: LottieDynamicProperties
-) {
- val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
- LottieAnimation(
- composition = composition,
- progress = { 0f }, // animation should freeze on 1st frame
- dynamicProperties = animationProperties,
- )
-}
-
-@Composable
-private fun EducationAnimation(
- @RawRes educationAnimationId: Int,
- animationProperties: LottieDynamicProperties
-) {
- val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
- val progress by
- animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
- LottieAnimation(
- composition = composition,
- progress = { progress },
- dynamicProperties = animationProperties,
- )
-}
-
-@Composable
-private fun SuccessAnimation(
- @RawRes successAnimationId: Int,
- animationProperties: LottieDynamicProperties
-) {
- val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
- val progress by animateLottieCompositionAsState(composition, iterations = 1)
- LottieAnimation(
- composition = composition,
- progress = { progress },
- dynamicProperties = animationProperties,
- )
-}
-
-@Composable
-fun rememberColorFilterProperty(
- layerName: String,
- color: Color
-): LottieDynamicProperty<ColorFilter> {
- return rememberLottieDynamicProperty(
- LottieProperty.COLOR_FILTER,
- value = PorterDuffColorFilter(color.toArgb(), PorterDuff.Mode.SRC_ATOP),
- // "**" below means match zero or more layers, so ** layerName ** means find layer with that
- // name at any depth
- keyPath = arrayOf("**", layerName, "**")
- )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
index ad8ab30..48e6397 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
@@ -27,9 +27,11 @@
import androidx.lifecycle.Lifecycle.State.STARTED
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.PlatformTheme
+import com.android.systemui.touchpad.tutorial.ui.composable.ActionKeyTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.BackGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.TutorialSelectionScreen
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.ACTION_KEY
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.BACK_GESTURE
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.HOME_GESTURE
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.TUTORIAL_SELECTION
@@ -71,7 +73,7 @@
TutorialSelectionScreen(
onBackTutorialClicked = { vm.goTo(BACK_GESTURE) },
onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) },
- onActionKeyTutorialClicked = {},
+ onActionKeyTutorialClicked = { vm.goTo(ACTION_KEY) },
onDoneButtonClicked = closeTutorial
)
BACK_GESTURE ->
@@ -84,5 +86,10 @@
onDoneButtonClicked = { vm.goTo(TUTORIAL_SELECTION) },
onBack = { vm.goTo(TUTORIAL_SELECTION) },
)
+ ACTION_KEY -> // TODO(b/358105049) move action key tutorial to OOBE flow
+ ActionKeyTutorialScreen(
+ onDoneButtonClicked = { vm.goTo(TUTORIAL_SELECTION) },
+ onBack = { vm.goTo(TUTORIAL_SELECTION) },
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
index 11984af..d3aeaa7 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
@@ -55,4 +55,5 @@
TUTORIAL_SELECTION,
BACK_GESTURE,
HOME_GESTURE,
+ ACTION_KEY,
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index 56b46624..32f2ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -116,7 +116,7 @@
return mCreateUserDialogController.createDialog(
this,
this::startActivity,
- (mUserCreator.isMultipleAdminEnabled() && mUserCreator.isUserAdmin()
+ (mUserCreator.canCreateAdminUser() && mUserCreator.isUserAdmin()
&& !isKeyguardShowing),
this::addUserNow,
this::finish
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
index 9304a46..a426da9 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.pm.UserInfo
import android.graphics.drawable.Drawable
+import android.multiuser.Flags
import android.os.UserManager
import com.android.internal.util.UserIcons
import com.android.settingslib.users.UserCreatingDialog
@@ -91,7 +92,17 @@
return userManager.isAdminUser
}
- fun isMultipleAdminEnabled(): Boolean {
- return UserManager.isMultipleAdminEnabled()
+ /**
+ * Checks if the creation of a new admin user is allowed.
+ *
+ * @return `true` if creating a new admin is allowed, `false` otherwise.
+ */
+ fun canCreateAdminUser(): Boolean {
+ return if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+ UserManager.isMultipleAdminEnabled() &&
+ !userManager.hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN)
+ } else {
+ UserManager.isMultipleAdminEnabled()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
index e17274c..ade6c3d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
@@ -19,11 +19,14 @@
import android.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.SafeActivatable
+import com.android.systemui.lifecycle.SysUiViewModel
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printCollection
import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
@@ -64,19 +67,20 @@
}
/**
- * An implementation of [FlowDumper]. This be extended directly, or can be used to implement
- * [FlowDumper] by delegation.
- *
- * @param dumpManager if provided, this will be used by the [FlowDumperImpl] to register and
- * unregister itself when there is something to dump.
- * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
- * class's name will be used. If you're implementing by delegation, you probably want to provide
- * this tag to get a meaningful dumpable name.
+ * The minimal implementation of FlowDumper. The owner must either register this with the
+ * DumpManager, or else call [dumpFlows] from its own [Dumpable.dump] method.
*/
-open class FlowDumperImpl(private val dumpManager: DumpManager?, tag: String? = null) : FlowDumper {
+open class SimpleFlowDumper : FlowDumper {
+
private val stateFlowMap = ConcurrentHashMap<String, StateFlow<*>>()
private val sharedFlowMap = ConcurrentHashMap<String, SharedFlow<*>>()
private val flowCollectionMap = ConcurrentHashMap<Pair<String, String>, Any>()
+
+ protected fun isNotEmpty(): Boolean =
+ stateFlowMap.isNotEmpty() || sharedFlowMap.isNotEmpty() || flowCollectionMap.isNotEmpty()
+
+ protected open fun onMapKeysChanged(added: Boolean) {}
+
override fun dumpFlows(pw: IndentingPrintWriter) {
pw.printCollection("StateFlow (value)", stateFlowMap.toSortedMap().entries) { (key, flow) ->
append(key).append('=').println(flow.value)
@@ -92,43 +96,62 @@
}
}
- private val Any.idString: String
- get() = Integer.toHexString(System.identityHashCode(this))
-
override fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T> = flow {
val mapKey = dumpName to idString
try {
collect {
flowCollectionMap[mapKey] = it ?: "null"
- updateRegistration(required = true)
+ onMapKeysChanged(added = true)
emit(it)
}
} finally {
flowCollectionMap.remove(mapKey)
- updateRegistration(required = false)
+ onMapKeysChanged(added = false)
}
}
override fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F {
stateFlowMap[dumpName] = this
+ onMapKeysChanged(added = true)
return this
}
override fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F {
sharedFlowMap[dumpName] = this
+ onMapKeysChanged(added = true)
return this
}
- private val dumpManagerName = tag ?: "[$idString] ${javaClass.simpleName}"
+ protected val Any.idString: String
+ get() = Integer.toHexString(System.identityHashCode(this))
+}
+
+/**
+ * An implementation of [FlowDumper] that registers itself whenever there is something to dump. This
+ * class is meant to be extended.
+ *
+ * @param dumpManager this will be used by the [FlowDumperImpl] to register and unregister itself
+ * when there is something to dump.
+ * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
+ * class's name will be used.
+ */
+abstract class FlowDumperImpl(
+ private val dumpManager: DumpManager,
+ private val tag: String? = null,
+) : SimpleFlowDumper() {
+
+ override fun onMapKeysChanged(added: Boolean) {
+ updateRegistration(required = added)
+ }
+
+ private val dumpManagerName = "[$idString] ${tag ?: javaClass.simpleName}"
+
private var registered = AtomicBoolean(false)
+
private fun updateRegistration(required: Boolean) {
- if (dumpManager == null) return
if (required && registered.get()) return
synchronized(registered) {
- val shouldRegister =
- stateFlowMap.isNotEmpty() ||
- sharedFlowMap.isNotEmpty() ||
- flowCollectionMap.isNotEmpty()
+ val shouldRegister = isNotEmpty()
val wasRegistered = registered.getAndSet(shouldRegister)
if (wasRegistered != shouldRegister) {
if (shouldRegister) {
@@ -140,3 +163,49 @@
}
}
}
+
+/**
+ * A [FlowDumper] that also has an [activateFlowDumper] suspend function that allows the dumper to
+ * be registered with the [DumpManager] only when activated, just like
+ * [Activatable.activate()][com.android.systemui.lifecycle.Activatable.activate].
+ */
+interface ActivatableFlowDumper : FlowDumper {
+ suspend fun activateFlowDumper()
+}
+
+/**
+ * Implementation of [ActivatableFlowDumper] that only registers when activated.
+ *
+ * This is generally used to implement [ActivatableFlowDumper] by delegation, especially for
+ * [SysUiViewModel] implementations.
+ *
+ * @param dumpManager used to automatically register and unregister this instance when activated and
+ * there is something to dump.
+ * @param tag the name with which this is dumper registered.
+ */
+class ActivatableFlowDumperImpl(
+ private val dumpManager: DumpManager,
+ tag: String,
+) : SimpleFlowDumper(), ActivatableFlowDumper {
+
+ private val registration =
+ object : SafeActivatable() {
+ override suspend fun onActivated() {
+ try {
+ dumpManager.registerCriticalDumpable(
+ dumpManagerName,
+ this@ActivatableFlowDumperImpl
+ )
+ awaitCancellation()
+ } finally {
+ dumpManager.unregisterDumpable(dumpManagerName)
+ }
+ }
+ }
+
+ private val dumpManagerName = "[$idString] $tag"
+
+ override suspend fun activateFlowDumper() {
+ registration.activate()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
index 816f55d..7fcabe4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -42,28 +42,31 @@
mBgDispatcher = bgDispatcher;
}
+ @NonNull
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
+ @NonNull
@Override
- public Uri getUriFor(String name) {
+ public Uri getUriFor(@NonNull String name) {
return Settings.Global.getUriFor(name);
}
+ @NonNull
@Override
public CoroutineDispatcher getBackgroundDispatcher() {
return mBgDispatcher;
}
@Override
- public String getString(String name) {
+ public String getString(@NonNull String name) {
return Settings.Global.getString(mContentResolver, name);
}
@Override
- public boolean putString(String name, String value) {
+ public boolean putString(@NonNull String name, String value) {
return Settings.Global.putString(mContentResolver, name, value);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
index f1da27f..c296481 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -16,12 +16,11 @@
package com.android.systemui.util.settings;
+import android.annotation.NonNull;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.Settings;
-import androidx.annotation.NonNull;
-
import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -43,46 +42,50 @@
mBgDispatcher = bgDispatcher;
}
+ @NonNull
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
+ @NonNull
@Override
public CurrentUserIdProvider getCurrentUserProvider() {
return mCurrentUserProvider;
}
+ @NonNull
@Override
- public Uri getUriFor(String name) {
+ public Uri getUriFor(@NonNull String name) {
return Settings.Secure.getUriFor(name);
}
+ @NonNull
@Override
public CoroutineDispatcher getBackgroundDispatcher() {
return mBgDispatcher;
}
@Override
- public String getStringForUser(String name, int userHandle) {
+ public String getStringForUser(@NonNull String name, int userHandle) {
return Settings.Secure.getStringForUser(mContentResolver, name,
getRealUserHandle(userHandle));
}
@Override
- public boolean putString(String name, String value, boolean overrideableByRestore) {
+ public boolean putString(@NonNull String name, String value, boolean overrideableByRestore) {
return Settings.Secure.putString(mContentResolver, name, value, overrideableByRestore);
}
@Override
- public boolean putStringForUser(String name, String value, int userHandle) {
+ public boolean putStringForUser(@NonNull String name, String value, int userHandle) {
return Settings.Secure.putStringForUser(mContentResolver, name, value,
getRealUserHandle(userHandle));
}
@Override
- public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
- int userHandle, boolean overrideableByRestore) {
+ public boolean putStringForUser(@NonNull String name, String value, String tag,
+ boolean makeDefault, int userHandle, boolean overrideableByRestore) {
return Settings.Secure.putStringForUser(
mContentResolver, name, value, tag, makeDefault, getRealUserHandle(userHandle),
overrideableByRestore);
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index 0ee997e..82f41a7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -346,7 +346,7 @@
* @param value to associate with the name
* @return true if the value was set, false on database errors
*/
- fun putString(name: String, value: String): Boolean
+ fun putString(name: String, value: String?): Boolean
/**
* Store a name/value pair into the database.
@@ -377,7 +377,7 @@
* @return true if the value was set, false on database errors.
* @see .resetToDefaults
*/
- fun putString(name: String, value: String, tag: String, makeDefault: Boolean): Boolean
+ fun putString(name: String, value: String?, tag: String?, makeDefault: Boolean): Boolean
/**
* Convenience function for retrieving a single secure settings value as an integer. Note that
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
index 1e80357..e670b2c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -16,12 +16,11 @@
package com.android.systemui.util.settings;
+import android.annotation.NonNull;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.Settings;
-import androidx.annotation.NonNull;
-
import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -42,46 +41,50 @@
mBgCoroutineDispatcher = bgDispatcher;
}
+ @NonNull
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
+ @NonNull
@Override
public CurrentUserIdProvider getCurrentUserProvider() {
return mCurrentUserProvider;
}
+ @NonNull
@Override
- public Uri getUriFor(String name) {
+ public Uri getUriFor(@NonNull String name) {
return Settings.System.getUriFor(name);
}
+ @NonNull
@Override
public CoroutineDispatcher getBackgroundDispatcher() {
return mBgCoroutineDispatcher;
}
@Override
- public String getStringForUser(String name, int userHandle) {
+ public String getStringForUser(@NonNull String name, int userHandle) {
return Settings.System.getStringForUser(mContentResolver, name,
getRealUserHandle(userHandle));
}
@Override
- public boolean putString(String name, String value, boolean overrideableByRestore) {
+ public boolean putString(@NonNull String name, String value, boolean overrideableByRestore) {
return Settings.System.putString(mContentResolver, name, value, overrideableByRestore);
}
@Override
- public boolean putStringForUser(String name, String value, int userHandle) {
+ public boolean putStringForUser(@NonNull String name, String value, int userHandle) {
return Settings.System.putStringForUser(mContentResolver, name, value,
getRealUserHandle(userHandle));
}
@Override
- public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
- int userHandle, boolean overrideableByRestore) {
+ public boolean putStringForUser(@NonNull String name, String value, String tag,
+ boolean makeDefault, int userHandle, boolean overrideableByRestore) {
throw new UnsupportedOperationException(
"This method only exists publicly for Settings.Secure and Settings.Global");
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
index 9ae8f03..8e3b813 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -368,19 +368,19 @@
* @param value to associate with the name
* @return true if the value was set, false on database errors
*/
- fun putString(name: String, value: String, overrideableByRestore: Boolean): Boolean
+ fun putString(name: String, value: String?, overrideableByRestore: Boolean): Boolean
- override fun putString(name: String, value: String): Boolean {
+ override fun putString(name: String, value: String?): Boolean {
return putStringForUser(name, value, userId)
}
/** Similar implementation to [putString] for the specified [userHandle]. */
- fun putStringForUser(name: String, value: String, userHandle: Int): Boolean
+ fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean
/** Similar implementation to [putString] for the specified [userHandle]. */
fun putStringForUser(
name: String,
- value: String,
+ value: String?,
tag: String?,
makeDefault: Boolean,
@UserIdInt userHandle: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
index e46ce26..24fb001 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
@@ -19,6 +19,7 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState
import java.io.PrintWriter
import javax.inject.Inject
@@ -27,10 +28,15 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
-private const val TAG = "VolumePanelGlobalState"
+private const val TAG = "VolumePanelGlobalStateRepository"
@SysUISingleton
-class VolumePanelGlobalStateRepository @Inject constructor(dumpManager: DumpManager) : Dumpable {
+class VolumePanelGlobalStateRepository
+@Inject
+constructor(
+ dumpManager: DumpManager,
+ private val logger: VolumePanelLogger,
+) : Dumpable {
private val mutableGlobalState =
MutableStateFlow(
@@ -48,6 +54,7 @@
update: (currentState: VolumePanelGlobalState) -> VolumePanelGlobalState
) {
mutableGlobalState.update(update)
+ logger.onVolumePanelGlobalStateChanged(mutableGlobalState.value)
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
index 5301b00..9de862a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
@@ -19,6 +19,7 @@
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import com.android.systemui.volume.panel.domain.model.ComponentModel
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import javax.inject.Inject
import javax.inject.Provider
@@ -26,8 +27,12 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
interface ComponentsInteractor {
@@ -45,6 +50,7 @@
enabledComponents: Collection<VolumePanelComponentKey>,
defaultCriteria: Provider<ComponentAvailabilityCriteria>,
@VolumePanelScope coroutineScope: CoroutineScope,
+ private val logger: VolumePanelLogger,
private val criteriaByKey:
Map<
VolumePanelComponentKey,
@@ -57,12 +63,18 @@
combine(
enabledComponents.map { componentKey ->
val componentCriteria = (criteriaByKey[componentKey] ?: defaultCriteria).get()
- componentCriteria.isAvailable().map { isAvailable ->
- ComponentModel(componentKey, isAvailable = isAvailable)
- }
+ componentCriteria
+ .isAvailable()
+ .distinctUntilChanged()
+ .conflate()
+ .onEach { logger.onComponentAvailabilityChanged(componentKey, it) }
+ .map { isAvailable ->
+ ComponentModel(componentKey, isAvailable = isAvailable)
+ }
}
) {
it.asList()
}
- .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1)
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
index cc513b5..276326c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
@@ -20,15 +20,41 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.VolumeLog
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
+import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState
+import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
import javax.inject.Inject
private const val TAG = "SysUI_VolumePanel"
/** Logs events related to the Volume Panel. */
-@VolumePanelScope
class VolumePanelLogger @Inject constructor(@VolumeLog private val logBuffer: LogBuffer) {
+ fun onVolumePanelStateChanged(state: VolumePanelState) {
+ logBuffer.log(TAG, LogLevel.DEBUG, { str1 = state.toString() }, { "State changed: $str1" })
+ }
+
+ fun onComponentAvailabilityChanged(key: VolumePanelComponentKey, isAvailable: Boolean) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = key
+ bool1 = isAvailable
+ },
+ { "$str1 isAvailable=$bool1" }
+ )
+ }
+
+ fun onVolumePanelGlobalStateChanged(globalState: VolumePanelGlobalState) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { bool1 = globalState.isVisible },
+ { "Global state changed: isVisible=$bool1" }
+ )
+ }
+
fun onSetVolumeRequested(audioStream: AudioStream, volume: Int) {
logBuffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
index 1c51236..a06d3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.ui.layout
import com.android.systemui.volume.panel.ui.viewmodel.ComponentState
+import com.android.systemui.volume.panel.ui.viewmodel.toLogString
/** Represents components grouping into the layout. */
data class ComponentsLayout(
@@ -29,3 +30,12 @@
/** This is a separated entity that is always visible on the bottom of the Volume Panel. */
val bottomBarComponent: ComponentState,
)
+
+fun ComponentsLayout.toLogString(): String {
+ return "(" +
+ " headerComponents=${headerComponents.joinToString { it.toLogString() }}" +
+ " contentComponents=${contentComponents.joinToString { it.toLogString() }}" +
+ " footerComponents=${footerComponents.joinToString { it.toLogString() }}" +
+ " bottomBarComponent=${bottomBarComponent.toLogString()}" +
+ " )"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
index 5f4dbfb..41c80fa 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
@@ -32,3 +32,5 @@
val component: VolumePanelUiComponent,
val isVisible: Boolean,
)
+
+fun ComponentState.toLogString(): String = "$key:visible=$isVisible"
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index f495a02f..2f60c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -19,23 +19,30 @@
import android.content.Context
import android.content.IntentFilter
import android.content.res.Resources
+import com.android.systemui.Dumpable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.util.kotlin.launchAndDispose
import com.android.systemui.volume.VolumePanelDialogReceiver
import com.android.systemui.volume.panel.dagger.VolumePanelComponent
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager
+import com.android.systemui.volume.panel.ui.layout.toLogString
+import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
@@ -43,19 +50,23 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
+private const val TAG = "VolumePanelViewModel"
+
// Can't inject a constructor here because VolumePanelComponent provides this view model for its
// components.
+@OptIn(ExperimentalCoroutinesApi::class)
class VolumePanelViewModel(
resources: Resources,
coroutineScope: CoroutineScope,
daggerComponentFactory: VolumePanelComponentFactory,
configurationController: ConfigurationController,
broadcastDispatcher: BroadcastDispatcher,
+ private val dumpManager: DumpManager,
+ private val logger: VolumePanelLogger,
private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
-) {
+) : Dumpable {
private val volumePanelComponent: VolumePanelComponent =
daggerComponentFactory.create(this, coroutineScope)
@@ -77,9 +88,10 @@
.onStart { emit(resources.configuration) }
.map { configuration ->
VolumePanelState(
- orientation = configuration.orientation,
- isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen),
- )
+ orientation = configuration.orientation,
+ isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen),
+ )
+ .also { logger.onVolumePanelStateChanged(it) }
}
.stateIn(
scope,
@@ -89,7 +101,7 @@
isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen)
),
)
- val componentsLayout: Flow<ComponentsLayout> =
+ val componentsLayout: StateFlow<ComponentsLayout?> =
combine(
componentsInteractor.components,
volumePanelState,
@@ -104,13 +116,18 @@
}
componentsLayoutManager.layout(scope, componentStates)
}
- .shareIn(
+ .stateIn(
scope,
SharingStarted.Eagerly,
- replay = 1,
+ null,
)
init {
+ scope.launchAndDispose {
+ dumpManager.registerNormalDumpable(TAG, this)
+ DisposableHandle { dumpManager.unregisterDumpable(TAG) }
+ }
+
volumePanelComponent.volumePanelStartables().onEach(VolumePanelStartable::start)
broadcastDispatcher
.broadcastFlow(IntentFilter(VolumePanelDialogReceiver.DISMISS_ACTION))
@@ -122,6 +139,13 @@
volumePanelGlobalStateInteractor.setVisible(false)
}
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ with(pw) {
+ println("volumePanelState=${volumePanelState.value}")
+ println("componentsLayout=${componentsLayout.value?.toLogString()}")
+ }
+ }
+
class Factory
@Inject
constructor(
@@ -129,6 +153,8 @@
private val daggerComponentFactory: VolumePanelComponentFactory,
private val configurationController: ConfigurationController,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val dumpManager: DumpManager,
+ private val logger: VolumePanelLogger,
private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
) {
@@ -139,6 +165,8 @@
daggerComponentFactory,
configurationController,
broadcastDispatcher,
+ dumpManager,
+ logger,
volumePanelGlobalStateInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
new file mode 100644
index 0000000..2ac5d10
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2024 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.accessibility.hearingaid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptyList;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHapClient;
+import android.bluetooth.BluetoothHapPresetInfo;
+import android.testing.TestableLooper;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HapClientProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** Tests for {@link HearingDevicesPresetsController}. */
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
+
+ private static final int TEST_PRESET_INDEX = 1;
+ private static final String TEST_PRESET_NAME = "test_preset";
+ private static final int TEST_HAP_GROUP_ID = 1;
+ private static final int TEST_REASON = 1024;
+
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private HapClientProfile mHapClientProfile;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private CachedBluetoothDevice mSubCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ @Mock
+ private BluetoothDevice mSubBluetoothDevice;
+
+ @Mock
+ private HearingDevicesPresetsController.PresetCallback mCallback;
+
+ private HearingDevicesPresetsController mController;
+
+ @Before
+ public void setUp() {
+ when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
+ when(mHapClientProfile.isProfileReady()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
+ when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
+
+ mController = new HearingDevicesPresetsController(mProfileManager, mCallback);
+ }
+
+ @Test
+ public void onServiceConnected_callExpectedCallback() {
+ mController.onServiceConnected();
+
+ verify(mHapClientProfile).registerCallback(any(Executor.class),
+ any(BluetoothHapClient.Callback.class));
+ verify(mCallback).onPresetInfoUpdated(anyList(), anyInt());
+ }
+
+ @Test
+ public void getAllPresetInfo_setInvalidHearingDevice_getEmpty() {
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(emptyList());
+ mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+
+ assertThat(mController.getAllPresetInfo()).isEmpty();
+ }
+
+ @Test
+ public void getAllPresetInfo_containsNotAvailablePresetInfo_getEmpty() {
+ setValidHearingDeviceSupportHap();
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(false);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+
+ assertThat(mController.getAllPresetInfo()).isEmpty();
+ }
+
+ @Test
+ public void getAllPresetInfo_containsOnePresetInfo_getOnePresetInfo() {
+ setValidHearingDeviceSupportHap();
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+
+ assertThat(mController.getAllPresetInfo()).contains(hapPresetInfo);
+ }
+
+ @Test
+ public void getActivePresetIndex_getExpectedIndex() {
+ setValidHearingDeviceSupportHap();
+ when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+ TEST_PRESET_INDEX);
+
+ assertThat(mController.getActivePresetIndex()).isEqualTo(TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void onPresetSelected_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+ setValidHearingDeviceSupportHap();
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+ when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+ TEST_PRESET_INDEX);
+
+ mController.onPresetSelected(mBluetoothDevice, TEST_PRESET_INDEX, TEST_REASON);
+
+ verify(mCallback).onPresetInfoUpdated(eq(List.of(hapPresetInfo)), eq(TEST_PRESET_INDEX));
+ }
+
+ @Test
+ public void onPresetInfoChanged_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+ setValidHearingDeviceSupportHap();
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+ when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+ TEST_PRESET_INDEX);
+
+ mController.onPresetInfoChanged(mBluetoothDevice, List.of(hapPresetInfo), TEST_REASON);
+
+ verify(mCallback).onPresetInfoUpdated(List.of(hapPresetInfo), TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void onPresetSelectionFailed_callOnPresetCommandFailed() {
+ setValidHearingDeviceSupportHap();
+
+ mController.onPresetSelectionFailed(mBluetoothDevice, TEST_REASON);
+
+ verify(mCallback).onPresetCommandFailed(TEST_REASON);
+ }
+
+ @Test
+ public void onSetPresetNameFailed_callOnPresetCommandFailed() {
+ setValidHearingDeviceSupportHap();
+
+ mController.onSetPresetNameFailed(mBluetoothDevice, TEST_REASON);
+
+ verify(mCallback).onPresetCommandFailed(TEST_REASON);
+ }
+
+ @Test
+ public void onPresetSelectionForGroupFailed_callSelectPresetIndividual() {
+ setValidHearingDeviceSupportHap();
+ mController.selectPreset(TEST_PRESET_INDEX);
+ Mockito.reset(mHapClientProfile);
+ when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+ mController.onPresetSelectionForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
+
+
+ verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void onSetPresetNameForGroupFailed_callOnPresetCommandFailed() {
+ setValidHearingDeviceSupportHap();
+
+ mController.onSetPresetNameForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
+
+ verify(mCallback).onPresetCommandFailed(TEST_REASON);
+ }
+
+ @Test
+ public void registerHapCallback_callHapRegisterCallback() {
+ mController.registerHapCallback();
+
+ verify(mHapClientProfile).registerCallback(any(Executor.class),
+ any(BluetoothHapClient.Callback.class));
+ }
+
+ @Test
+ public void unregisterHapCallback_callHapUnregisterCallback() {
+ mController.unregisterHapCallback();
+
+ verify(mHapClientProfile).unregisterCallback(any(BluetoothHapClient.Callback.class));
+ }
+
+ @Test
+ public void selectPreset_supportSynchronized_validGroupId_callSelectPresetForGroup() {
+ setValidHearingDeviceSupportHap();
+ when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
+ when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+ mController.selectPreset(TEST_PRESET_INDEX);
+
+ verify(mHapClientProfile).selectPresetForGroup(TEST_HAP_GROUP_ID, TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void selectPreset_supportSynchronized_invalidGroupId_callSelectPresetIndividual() {
+ setValidHearingDeviceSupportHap();
+ when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
+ when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+
+ mController.selectPreset(TEST_PRESET_INDEX);
+
+ verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void selectPreset_notSupportSynchronized_validGroupId_callSelectPresetIndividual() {
+ setValidHearingDeviceSupportHap();
+ when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(false);
+ when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+ mController.selectPreset(TEST_PRESET_INDEX);
+
+ verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ }
+
+ private BluetoothHapPresetInfo getHapPresetInfo(boolean available) {
+ BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class);
+ when(info.getName()).thenReturn(TEST_PRESET_NAME);
+ when(info.getIndex()).thenReturn(TEST_PRESET_INDEX);
+ when(info.isAvailable()).thenReturn(available);
+ return info;
+ }
+
+ private void setValidHearingDeviceSupportHap() {
+ LocalBluetoothProfile hapClientProfile = mock(HapClientProfile.class);
+ List<LocalBluetoothProfile> profiles = List.of(hapClientProfile);
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(profiles);
+
+ mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index 313292a..d8e96bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -51,14 +51,14 @@
@Test
fun showPrimaryBouncer() =
testScope.runTest {
- underTest.showPrimaryBouncer()
+ underTest.onTapped()
verify(statusBarKeyguardViewManager).showPrimaryBouncer(any())
}
@Test
fun hideAlternateBouncer() =
testScope.runTest {
- underTest.hideAlternateBouncer()
+ underTest.onRemovedFromWindow()
verify(statusBarKeyguardViewManager).hideAlternateBouncer(any())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 77977f3..24bea2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -195,9 +195,7 @@
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
val featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false)
- }
+ FakeFeatureFlags().apply { set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) }
val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
keyguardInteractor = withDeps.keyguardInteractor
@@ -289,6 +287,7 @@
underTest =
KeyguardQuickAffordancesCombinedViewModel(
+ applicationScope = testScope.backgroundScope,
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
index b337ccf..8fbd3c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
@@ -24,7 +24,6 @@
import android.view.ViewGroup
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX
import com.android.systemui.Flags.FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
@@ -141,27 +140,13 @@
}
@Test
- fun onRecentAppClicked_fullScreenTaskInForeground_flagOff_usesScaleUpAnimation() {
- mSetFlagsRule.disableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
-
- controller.onRecentAppClicked(fullScreenTask, taskView)
-
- assertThat(getStartedTaskActivityOptions(fullScreenTask.taskId).animationType)
- .isEqualTo(ActivityOptions.ANIM_SCALE_UP)
- }
-
- @Test
- fun onRecentAppClicked_fullScreenTaskInForeground_flagOn_usesDefaultAnimation() {
- mSetFlagsRule.enableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
+ fun onRecentAppClicked_fullScreenTaskInForeground_usesDefaultAnimation() {
assertForegroundTaskUsesDefaultCloseAnimation(fullScreenTask)
}
@Test
fun onRecentAppClicked_splitScreenTaskInForeground_flagOn_usesDefaultAnimation() {
- mSetFlagsRule.enableFlags(
- FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX,
- FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN
- )
+ mSetFlagsRule.enableFlags(FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN)
assertForegroundTaskUsesDefaultCloseAnimation(splitScreenTask)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index a52ab0c..04d140c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -78,6 +78,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
@@ -214,6 +215,8 @@
@Mock
private WindowManager mWindowManager;
@Mock
+ private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ @Mock
private TelecomManager mTelecomManager;
@Mock
private InputMethodManager mInputMethodManager;
@@ -619,6 +622,7 @@
null,
context,
mWindowManager,
+ mViewCaptureAwareWindowManager,
() -> mAssistManager,
mock(AccessibilityManager.class),
deviceProvisionedController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 90e0dd8..0c2b59f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
@@ -17,25 +17,34 @@
package com.android.systemui.qs
import android.os.Handler
+import android.platform.test.flag.junit.FlagsParameterization
import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.fail
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
private typealias Callback = (Int, Boolean) -> Unit
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper
-class UserSettingObserverTest : SysuiTestCase() {
+class UserSettingObserverTest(flags: FlagsParameterization) : SysuiTestCase() {
companion object {
private const val TEST_SETTING = "setting"
@@ -43,8 +52,23 @@
private const val OTHER_USER = 1
private const val DEFAULT_VALUE = 1
private val FAIL_CALLBACK: Callback = { _, _ -> fail("Callback should not be called") }
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_QS_REGISTER_SETTING_OBSERVER_ON_BG_THREAD
+ )
+ }
}
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+
private lateinit var testableLooper: TestableLooper
private lateinit var setting: UserSettingObserver
private lateinit var secureSettings: SecureSettings
@@ -54,7 +78,7 @@
@Before
fun setUp() {
testableLooper = TestableLooper.get(this)
- secureSettings = FakeSettings()
+ secureSettings = kosmos.fakeSettings
setting =
object :
@@ -76,92 +100,107 @@
@After
fun tearDown() {
- setting.isListening = false
+ setListening(false)
}
@Test
- fun testNotListeningByDefault() {
- callback = FAIL_CALLBACK
+ fun testNotListeningByDefault() =
+ testScope.runTest {
+ callback = FAIL_CALLBACK
- assertThat(setting.isListening).isFalse()
- secureSettings.putIntForUser(TEST_SETTING, 2, USER)
- testableLooper.processAllMessages()
- }
+ assertThat(setting.isListening).isFalse()
+ secureSettings.putIntForUser(TEST_SETTING, 2, USER)
+ testableLooper.processAllMessages()
+ }
@Test
- fun testChangedWhenListeningCallsCallback() {
- var changed = false
- callback = { _, _ -> changed = true }
+ fun testChangedWhenListeningCallsCallback() =
+ testScope.runTest {
+ var changed = false
+ callback = { _, _ -> changed = true }
- setting.isListening = true
- secureSettings.putIntForUser(TEST_SETTING, 2, USER)
- testableLooper.processAllMessages()
+ setListening(true)
+ secureSettings.putIntForUser(TEST_SETTING, 2, USER)
+ testableLooper.processAllMessages()
- assertThat(changed).isTrue()
- }
+ assertThat(changed).isTrue()
+ }
@Test
- fun testListensToCorrectSetting() {
- callback = FAIL_CALLBACK
+ fun testListensToCorrectSetting() =
+ testScope.runTest {
+ callback = FAIL_CALLBACK
- setting.isListening = true
- secureSettings.putIntForUser("other", 2, USER)
- testableLooper.processAllMessages()
- }
+ setListening(true)
+ secureSettings.putIntForUser("other", 2, USER)
+ testableLooper.processAllMessages()
+ }
@Test
- fun testGetCorrectValue() {
- secureSettings.putIntForUser(TEST_SETTING, 2, USER)
- assertThat(setting.value).isEqualTo(2)
+ fun testGetCorrectValue() =
+ testScope.runTest {
+ secureSettings.putIntForUser(TEST_SETTING, 2, USER)
+ assertThat(setting.value).isEqualTo(2)
- secureSettings.putIntForUser(TEST_SETTING, 4, USER)
- assertThat(setting.value).isEqualTo(4)
- }
+ secureSettings.putIntForUser(TEST_SETTING, 4, USER)
+ assertThat(setting.value).isEqualTo(4)
+ }
@Test
- fun testSetValue() {
- setting.value = 5
- assertThat(secureSettings.getIntForUser(TEST_SETTING, USER)).isEqualTo(5)
- }
+ fun testSetValue() =
+ testScope.runTest {
+ setting.value = 5
+ assertThat(secureSettings.getIntForUser(TEST_SETTING, USER)).isEqualTo(5)
+ }
@Test
- fun testChangeUser() {
- setting.isListening = true
- setting.setUserId(OTHER_USER)
+ fun testChangeUser() =
+ testScope.runTest {
+ setListening(true)
+ setting.setUserId(OTHER_USER)
- setting.isListening = true
- assertThat(setting.currentUser).isEqualTo(OTHER_USER)
- }
+ setListening(true)
+ assertThat(setting.currentUser).isEqualTo(OTHER_USER)
+ }
@Test
- fun testDoesntListenInOtherUsers() {
- callback = FAIL_CALLBACK
- setting.isListening = true
+ fun testDoesntListenInOtherUsers() =
+ testScope.runTest {
+ callback = FAIL_CALLBACK
+ setListening(true)
- secureSettings.putIntForUser(TEST_SETTING, 3, OTHER_USER)
- testableLooper.processAllMessages()
- }
+ secureSettings.putIntForUser(TEST_SETTING, 3, OTHER_USER)
+ testableLooper.processAllMessages()
+ }
@Test
- fun testListensToCorrectUserAfterChange() {
- var changed = false
- callback = { _, _ -> changed = true }
+ fun testListensToCorrectUserAfterChange() =
+ testScope.runTest {
+ var changed = false
+ callback = { _, _ -> changed = true }
- setting.isListening = true
- setting.setUserId(OTHER_USER)
- secureSettings.putIntForUser(TEST_SETTING, 2, OTHER_USER)
- testableLooper.processAllMessages()
+ setListening(true)
+ setting.setUserId(OTHER_USER)
+ testScope.runCurrent()
+ secureSettings.putIntForUser(TEST_SETTING, 2, OTHER_USER)
+ testableLooper.processAllMessages()
- assertThat(changed).isTrue()
- }
+ assertThat(changed).isTrue()
+ }
@Test
- fun testDefaultValue() {
- // Check default value before listening
- assertThat(setting.value).isEqualTo(DEFAULT_VALUE)
+ fun testDefaultValue() =
+ testScope.runTest {
+ // Check default value before listening
+ assertThat(setting.value).isEqualTo(DEFAULT_VALUE)
- // Check default value if setting is not set
- setting.isListening = true
- assertThat(setting.value).isEqualTo(DEFAULT_VALUE)
+ // Check default value if setting is not set
+ setListening(true)
+ assertThat(setting.value).isEqualTo(DEFAULT_VALUE)
+ }
+
+ fun setListening(listening: Boolean) {
+ setting.isListening = listening
+ testScope.runCurrent()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 79c206c..3f550ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -16,8 +16,13 @@
package com.android.systemui.screenrecord;
+import static com.android.systemui.screenrecord.RecordingService.GROUP_KEY_ERROR_SAVING;
+import static com.android.systemui.screenrecord.RecordingService.GROUP_KEY_SAVED;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -25,6 +30,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -73,8 +79,6 @@
@Mock
private ScreenMediaRecorder mScreenMediaRecorder;
@Mock
- private Notification mNotification;
- @Mock
private Executor mExecutor;
@Mock
private Handler mHandler;
@@ -124,10 +128,6 @@
// Mock notifications
doNothing().when(mRecordingService).createRecordingNotification();
- doReturn(mNotification).when(mRecordingService).createProcessingNotification();
- doReturn(mNotification).when(mRecordingService).createSaveNotification(any());
- doNothing().when(mRecordingService).createErrorStartingNotification();
- doNothing().when(mRecordingService).createErrorSavingNotification();
doNothing().when(mRecordingService).showErrorToast(anyInt());
doNothing().when(mRecordingService).stopForeground(anyInt());
@@ -228,6 +228,33 @@
}
@Test
+ public void testOnSystemRequestedStop_whenRecordingInProgress_showsNotifications() {
+ doReturn(true).when(mController).isRecording();
+
+ mRecordingService.onStopped();
+
+ // Processing notification
+ ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class);
+ verify(mNotificationManager).notifyAsUser(any(), anyInt(), notifCaptor.capture(), any());
+ assertEquals(GROUP_KEY_SAVED, notifCaptor.getValue().getGroup());
+
+ reset(mNotificationManager);
+ verify(mExecutor).execute(mRunnableCaptor.capture());
+ mRunnableCaptor.getValue().run();
+
+ verify(mNotificationManager, times(2))
+ .notifyAsUser(any(), anyInt(), notifCaptor.capture(), any());
+ // Saved notification
+ Notification saveNotification = notifCaptor.getAllValues().get(0);
+ assertFalse(saveNotification.isGroupSummary());
+ assertEquals(GROUP_KEY_SAVED, saveNotification.getGroup());
+ // Group summary notification
+ Notification groupSummaryNotification = notifCaptor.getAllValues().get(1);
+ assertTrue(groupSummaryNotification.isGroupSummary());
+ assertEquals(GROUP_KEY_SAVED, groupSummaryNotification.getGroup());
+ }
+
+ @Test
public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_showsErrorNotification()
throws IOException {
doReturn(true).when(mController).isRecording();
@@ -235,7 +262,11 @@
mRecordingService.onStopped();
- verify(mRecordingService).createErrorSavingNotification();
+ verify(mRecordingService).createErrorSavingNotification(any());
+ ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class);
+ verify(mNotificationManager).notifyAsUser(any(), anyInt(), notifCaptor.capture(), any());
+ assertTrue(notifCaptor.getValue().isGroupSummary());
+ assertEquals(GROUP_KEY_ERROR_SAVING, notifCaptor.getValue().getGroup());
}
@Test
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 2803035..8125ef5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -192,6 +192,7 @@
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
@@ -428,6 +429,9 @@
mock(DeviceEntryUdfpsInteractor.class);
when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(MutableStateFlow(false));
+ final SplitShadeStateController splitShadeStateController =
+ new ResourcesSplitShadeStateController();
+
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
mKosmos.getDeviceProvisioningInteractor(),
@@ -445,7 +449,8 @@
new SharedNotificationContainerInteractor(
new FakeConfigurationRepository(),
mContext,
- new ResourcesSplitShadeStateController(),
+ () -> splitShadeStateController,
+ () -> mShadeInteractor,
mKeyguardInteractor,
deviceEntryUdfpsInteractor,
() -> mLargeScreenHeaderHelper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index e57382d..505f799 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -225,7 +225,8 @@
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
- splitShadeStateController,
+ () -> splitShadeStateController,
+ () -> mShadeInteractor,
keyguardInteractor,
deviceEntryUdfpsInteractor,
() -> mLargeScreenHeaderHelper),
@@ -266,6 +267,7 @@
when(mPanelView.getParent()).thenReturn(mPanelViewParent);
when(mQs.getHeader()).thenReturn(mQsHeader);
+ when(mQSFragment.getHeader()).thenReturn(mQsHeader);
doAnswer(invocation -> {
mLockscreenShadeTransitionCallback = invocation.getArgument(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
index e7db469..2e9d6e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
@@ -24,34 +24,41 @@
import static android.view.MotionEvent.BUTTON_SECONDARY;
import static android.view.MotionEvent.BUTTON_STYLUS_PRIMARY;
-import static com.android.systemui.Flags.FLAG_QS_UI_REFACTOR;
import static com.android.systemui.Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
import android.view.MotionEvent;
+import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import java.util.List;
@@ -65,7 +72,7 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return progressionOf(FLAG_QS_UI_REFACTOR, FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT);
+ return progressionOf(FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT);
}
public QuickSettingsControllerImplTest(FlagsParameterization flags) {
@@ -244,6 +251,61 @@
}
@Test
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ public void onQsFragmentAttached_qsComposeFragmentDisabled_setHeaderInNSSL() {
+ mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+ verify(mNotificationStackScrollLayoutController)
+ .setQsHeader((ViewGroup) mQSFragment.getHeader());
+ verify(mNotificationStackScrollLayoutController, never()).setQsHeaderBoundsProvider(any());
+ }
+
+ @Test
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ public void onQsFragmentAttached_qsComposeFragmentEnabled_setQsHeaderBoundsProviderInNSSL() {
+ mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+ verify(mNotificationStackScrollLayoutController, never())
+ .setQsHeader((ViewGroup) mQSFragment.getHeader());
+ ArgumentCaptor<QSHeaderBoundsProvider> argumentCaptor =
+ ArgumentCaptor.forClass(QSHeaderBoundsProvider.class);
+
+ verify(mNotificationStackScrollLayoutController)
+ .setQsHeaderBoundsProvider(argumentCaptor.capture());
+
+ argumentCaptor.getValue().getLeftProvider().invoke();
+ argumentCaptor.getValue().getHeightProvider().invoke();
+ argumentCaptor.getValue().getBoundsOnScreenProvider().invoke(new Rect());
+ InOrder inOrderVerifier = inOrder(mQSFragment);
+
+ inOrderVerifier.verify(mQSFragment).getHeaderLeft();
+ inOrderVerifier.verify(mQSFragment).getHeaderHeight();
+ inOrderVerifier.verify(mQSFragment).getHeaderBoundsOnScreen(new Rect());
+ }
+
+ @Test
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ public void onQSFragmentDetached_qsComposeFragmentFlagDisabled_setViewToNullInNSSL() {
+ mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+ mFragmentListener.onFragmentViewDestroyed(QS.TAG, mQSFragment);
+
+ verify(mNotificationStackScrollLayoutController).setQsHeader(null);
+ verify(mNotificationStackScrollLayoutController, never()).setQsHeaderBoundsProvider(null);
+ }
+
+ @Test
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ public void onQSFragmentDetached_qsComposeFragmentFlagEnabled_setBoundsProviderToNullInNSSL() {
+ mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+ mFragmentListener.onFragmentViewDestroyed(QS.TAG, mQSFragment);
+
+ verify(mNotificationStackScrollLayoutController, never()).setQsHeader(null);
+ verify(mNotificationStackScrollLayoutController).setQsHeaderBoundsProvider(null);
+ }
+
+ @Test
public void onQsFragmentAttached_notFullWidth_setsFullWidthFalseOnQS() {
setIsFullWidth(false);
mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 3f28164..491919a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel
+import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction
import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews
import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
@@ -78,7 +79,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
-@EnableFlags(NotificationRowContentBinderRefactor.FLAG_NAME)
+@EnableFlags(NotificationRowContentBinderRefactor.FLAG_NAME, LockscreenOtpRedaction.FLAG_NAME)
class NotificationRowContentBinderImplTest : SysuiTestCase() {
private lateinit var notificationInflater: NotificationRowContentBinderImpl
private lateinit var builder: Notification.Builder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index af5e60e..9b61105 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -1068,7 +1068,7 @@
public void testShowBouncerOrKeyguard_needsFullScreen() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false);
verify(mCentralSurfaces).hideKeyguard();
verify(mPrimaryBouncerInteractor).show(true);
}
@@ -1084,7 +1084,7 @@
.thenReturn(KeyguardState.LOCKSCREEN);
reset(mCentralSurfaces);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false);
verify(mPrimaryBouncerInteractor).show(true);
verify(mCentralSurfaces).showKeyguard();
}
@@ -1092,11 +1092,26 @@
@Test
@DisableSceneContainer
public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing() {
+ boolean isFalsingReset = false;
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset);
verify(mCentralSurfaces, never()).hideKeyguard();
+ verify(mPrimaryBouncerInteractor).show(true);
+ }
+
+ @Test
+ @DisableSceneContainer
+ public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing_onFalsing() {
+ boolean isFalsingReset = true;
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPin);
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset);
+ verify(mCentralSurfaces, never()).hideKeyguard();
+
+ // Do not refresh the full screen bouncer if the call is from falsing
verify(mPrimaryBouncerInteractor, never()).show(true);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
index b0acd03..2e6d0fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -385,7 +385,7 @@
private class FakeSettingsProxy(val testDispatcher: CoroutineDispatcher) : SettingsProxy {
private val mContentResolver = mock(ContentResolver::class.java)
- private val settingToValueMap: MutableMap<String, String> = mutableMapOf()
+ private val settingToValueMap: MutableMap<String, String?> = mutableMapOf()
override fun getContentResolver() = mContentResolver
@@ -399,15 +399,15 @@
return settingToValueMap[name] ?: ""
}
- override fun putString(name: String, value: String): Boolean {
+ override fun putString(name: String, value: String?): Boolean {
settingToValueMap[name] = value
return true
}
override fun putString(
name: String,
- value: String,
- tag: String,
+ value: String?,
+ tag: String?,
makeDefault: Boolean
): Boolean {
settingToValueMap[name] = value
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
index eaeece9..00b8cd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -561,7 +561,7 @@
) : UserSettingsProxy {
private val mContentResolver = mock(ContentResolver::class.java)
- private val userIdToSettingsValueMap: MutableMap<Int, MutableMap<String, String>> =
+ private val userIdToSettingsValueMap: MutableMap<Int, MutableMap<String, String?>> =
mutableMapOf()
override fun getContentResolver() = mContentResolver
@@ -577,7 +577,7 @@
override fun putString(
name: String,
- value: String,
+ value: String?,
overrideableByRestore: Boolean
): Boolean {
userIdToSettingsValueMap[DEFAULT_USER_ID]?.put(name, value)
@@ -586,22 +586,22 @@
override fun putString(
name: String,
- value: String,
- tag: String,
+ value: String?,
+ tag: String?,
makeDefault: Boolean
): Boolean {
putStringForUser(name, value, DEFAULT_USER_ID)
return true
}
- override fun putStringForUser(name: String, value: String, userHandle: Int): Boolean {
+ override fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean {
userIdToSettingsValueMap[userHandle] = mutableMapOf(Pair(name, value))
return true
}
override fun putStringForUser(
name: String,
- value: String,
+ value: String?,
tag: String?,
makeDefault: Boolean,
userHandle: Int,
diff --git a/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
index 185deea..a61233a 100644
--- a/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt
@@ -16,10 +16,12 @@
package android.content
+import com.android.systemui.SysuiTestableContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.util.mockito.mock
-var Kosmos.applicationContext: Context by
+var Kosmos.testableContext: SysuiTestableContext by
Kosmos.Fixture { testCase.context.apply { ensureTestableResources() } }
+var Kosmos.applicationContext: Context by Kosmos.Fixture { testableContext }
val Kosmos.mockedContext: Context by Kosmos.Fixture { mock<Context>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index a1c2f79..4571c19 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -74,6 +74,8 @@
private val _dozeTimeTick = MutableStateFlow<Long>(0L)
override val dozeTimeTick = _dozeTimeTick
+ override val showDismissibleKeyguard = MutableStateFlow<Long>(0L)
+
private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
@@ -206,6 +208,10 @@
_dozeTimeTick.value = millis
}
+ override fun showDismissibleKeyguard() {
+ showDismissibleKeyguard.value = showDismissibleKeyguard.value + 1
+ }
+
override fun setLastDozeTapToWakePosition(position: Point) {
_lastDozeTapToWakePosition.value = position
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index c5da10e..b68d6a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -33,6 +33,7 @@
fromAodTransitionInteractor = { fromAodTransitionInteractor },
fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor },
fromDozingTransitionInteractor = { fromDozingTransitionInteractor },
+ fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor },
sceneInteractor = sceneInteractor
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
index 2919d3f..1e95fc1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.binder
import android.content.applicationContext
-import android.view.layoutInflater
import android.view.mockedLayoutInflater
import android.view.windowManager
import com.android.systemui.biometrics.domain.interactor.fingerprintPropertyInteractor
@@ -25,7 +24,6 @@
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
-import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
@@ -50,10 +48,10 @@
alternateBouncerDependencies = { alternateBouncerDependencies },
windowManager = { windowManager },
layoutInflater = { mockedLayoutInflater },
- dismissCallbackRegistry = dismissCallbackRegistry,
)
}
+@ExperimentalCoroutinesApi
private val Kosmos.alternateBouncerDependencies by
Kosmos.Fixture {
AlternateBouncerDependencies(
@@ -69,6 +67,7 @@
)
}
+@ExperimentalCoroutinesApi
private val Kosmos.alternateBouncerUdfpsIconViewModel by
Kosmos.Fixture {
AlternateBouncerUdfpsIconViewModel(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
index bdd4afa..2958315 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
@@ -18,6 +18,8 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,5 +30,7 @@
AlternateBouncerViewModel(
statusBarKeyguardViewManager = statusBarKeyguardViewManager,
keyguardTransitionInteractor = keyguardTransitionInteractor,
+ dismissCallbackRegistry = dismissCallbackRegistry,
+ alternateBouncerInteractor = { alternateBouncerInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 82860fc..b9443bc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -39,6 +39,7 @@
communalInteractor = communalInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
notificationsKeyguardInteractor = notificationsKeyguardInteractor,
+ alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
alternateBouncerToLockscreenTransitionViewModel =
alternateBouncerToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 8e76a0b..53b6a2e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -18,6 +18,7 @@
import com.android.internal.logging.uiEventLogger
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
import com.android.systemui.classifier.falsingCollector
@@ -80,5 +81,6 @@
keyguardEnabledInteractor = keyguardEnabledInteractor,
dismissCallbackRegistry = dismissCallbackRegistry,
statusBarStateController = sysuiStatusBarStateController,
+ alternateBouncerInteractor = alternateBouncerInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
index ea02d0c..6d488d2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
@@ -18,10 +18,13 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.SysuiTestableContext
+import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.data.repository.ShadeRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
@@ -86,6 +89,11 @@
delegate.assertFlagValid()
delegate.setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer)
}
+
+ fun setSplitShade(splitShade: Boolean) {
+ delegate.assertFlagValid()
+ delegate.setSplitShade(splitShade)
+ }
}
/** Sets up shade state for tests for a specific value of the scene container flag. */
@@ -117,11 +125,16 @@
fun setQsFullscreen(qsFullscreen: Boolean)
fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean)
+
+ fun setSplitShade(splitShade: Boolean)
}
/** Sets up shade state for tests when the scene container flag is disabled. */
-class ShadeTestUtilLegacyImpl(val testScope: TestScope, val shadeRepository: FakeShadeRepository) :
- ShadeTestUtilDelegate {
+class ShadeTestUtilLegacyImpl(
+ val testScope: TestScope,
+ val shadeRepository: FakeShadeRepository,
+ val context: SysuiTestableContext
+) : ShadeTestUtilDelegate {
override fun setShadeAndQsExpansion(shadeExpansion: Float, qsExpansion: Float) {
shadeRepository.setLegacyShadeExpansion(shadeExpansion)
shadeRepository.setQsExpansion(qsExpansion)
@@ -168,11 +181,22 @@
override fun setLegacyExpandedOrAwaitingInputTransfer(expanded: Boolean) {
shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(expanded)
}
+
+ override fun setSplitShade(splitShade: Boolean) {
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.bool.config_use_split_notification_shade, splitShade)
+ testScope.runCurrent()
+ }
}
/** Sets up shade state for tests when the scene container flag is enabled. */
-class ShadeTestUtilSceneImpl(val testScope: TestScope, val sceneInteractor: SceneInteractor) :
- ShadeTestUtilDelegate {
+class ShadeTestUtilSceneImpl(
+ val testScope: TestScope,
+ val sceneInteractor: SceneInteractor,
+ val shadeRepository: ShadeRepository,
+ val context: SysuiTestableContext,
+) : ShadeTestUtilDelegate {
val isUserInputOngoing = MutableStateFlow(true)
override fun setShadeAndQsExpansion(shadeExpansion: Float, qsExpansion: Float) {
@@ -263,6 +287,14 @@
testScope.runCurrent()
}
+ override fun setSplitShade(splitShade: Boolean) {
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.bool.config_use_split_notification_shade, splitShade)
+ shadeRepository.setShadeLayoutWide(splitShade)
+ testScope.runCurrent()
+ }
+
override fun assertFlagValid() {
Assert.assertTrue(SceneContainerFlag.isEnabled)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtilKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtilKosmos.kt
index 9eeb345..a1551e0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtilKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtilKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade
+import android.content.testableContext
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -26,9 +27,14 @@
Kosmos.Fixture {
ShadeTestUtil(
if (SceneContainerFlag.isEnabled) {
- ShadeTestUtilSceneImpl(testScope, sceneInteractor)
+ ShadeTestUtilSceneImpl(
+ testScope,
+ sceneInteractor,
+ fakeShadeRepository,
+ testableContext
+ )
} else {
- ShadeTestUtilLegacyImpl(testScope, fakeShadeRepository)
+ ShadeTestUtilLegacyImpl(testScope, fakeShadeRepository, testableContext)
}
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index bfd6614..54208b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -44,7 +44,7 @@
ShadeInteractorSceneContainerImpl(
scope = applicationCoroutineScope,
sceneInteractor = sceneInteractor,
- sharedNotificationContainerInteractor = sharedNotificationContainerInteractor,
+ shadeRepository = shadeRepository,
)
}
val Kosmos.shadeInteractorLegacyImpl by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
index 8909d75..3234e66 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.policy.splitShadeStateController
@@ -29,7 +30,8 @@
SharedNotificationContainerInteractor(
configurationRepository = configurationRepository,
context = applicationContext,
- splitShadeStateController = splitShadeStateController,
+ splitShadeStateController = { splitShadeStateController },
+ shadeInteractor = { shadeInteractor },
keyguardInteractor = keyguardInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
deleted file mode 100644
index d117466..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.util.settings;
-
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
-
-import android.annotation.UserIdInt;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import kotlinx.coroutines.CoroutineDispatcher;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class FakeSettings implements SecureSettings, SystemSettings {
- private final Map<SettingsKey, String> mValues = new HashMap<>();
- private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
- new HashMap<>();
- private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
- private final CoroutineDispatcher mDispatcher;
-
- public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
- @UserIdInt
- private int mUserId = UserHandle.USER_CURRENT;
-
- private final CurrentUserIdProvider mCurrentUserProvider;
-
- /**
- * @deprecated Please use FakeSettings(testDispatcher) to provide the same dispatcher used
- * by main test scope.
- */
- @Deprecated
- public FakeSettings() {
- mDispatcher = StandardTestDispatcher(/* scheduler = */ null, /* name = */ null);
- mCurrentUserProvider = () -> mUserId;
- }
-
- public FakeSettings(CoroutineDispatcher dispatcher) {
- mDispatcher = dispatcher;
- mCurrentUserProvider = () -> mUserId;
- }
-
- public FakeSettings(CoroutineDispatcher dispatcher, CurrentUserIdProvider currentUserProvider) {
- mDispatcher = dispatcher;
- mCurrentUserProvider = currentUserProvider;
- }
-
- @VisibleForTesting
- FakeSettings(String initialKey, String initialValue) {
- this();
- putString(initialKey, initialValue);
- }
-
- @VisibleForTesting
- FakeSettings(Map<String, String> initialValues) {
- this();
- for (Map.Entry<String, String> kv : initialValues.entrySet()) {
- putString(kv.getKey(), kv.getValue());
- }
- }
-
- @Override
- @NonNull
- public ContentResolver getContentResolver() {
- throw new UnsupportedOperationException(
- "FakeSettings.getContentResolver is not implemented");
- }
-
- @NonNull
- @Override
- public CurrentUserIdProvider getCurrentUserProvider() {
- return mCurrentUserProvider;
- }
-
- @NonNull
- @Override
- public CoroutineDispatcher getBackgroundDispatcher() {
- return mDispatcher;
- }
-
- @Override
- public void registerContentObserverForUserSync(@NonNull Uri uri, boolean notifyDescendants,
- @NonNull ContentObserver settingsObserver, int userHandle) {
- List<ContentObserver> observers;
- if (userHandle == UserHandle.USER_ALL) {
- mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
- observers = mContentObserversAllUsers.get(uri.toString());
- } else {
- SettingsKey key = new SettingsKey(userHandle, uri.toString());
- mContentObservers.putIfAbsent(key, new ArrayList<>());
- observers = mContentObservers.get(key);
- }
- observers.add(settingsObserver);
- }
-
- @Override
- public void unregisterContentObserverSync(@NonNull ContentObserver settingsObserver) {
- for (List<ContentObserver> observers : mContentObservers.values()) {
- observers.remove(settingsObserver);
- }
- for (List<ContentObserver> observers : mContentObserversAllUsers.values()) {
- observers.remove(settingsObserver);
- }
- }
-
- @NonNull
- @Override
- public Uri getUriFor(@NonNull String name) {
- return Uri.withAppendedPath(CONTENT_URI, name);
- }
-
- public void setUserId(@UserIdInt int userId) {
- mUserId = userId;
- }
-
- @Override
- public int getUserId() {
- return mUserId;
- }
-
- @Override
- public String getString(@NonNull String name) {
- return getStringForUser(name, getUserId());
- }
-
- @Override
- public String getStringForUser(@NonNull String name, int userHandle) {
- return mValues.get(new SettingsKey(userHandle, getUriFor(name).toString()));
- }
-
- @Override
- public boolean putString(@NonNull String name, @NonNull String value,
- boolean overrideableByRestore) {
- return putStringForUser(name, value, null, false, getUserId(), overrideableByRestore);
- }
-
- @Override
- public boolean putString(@NonNull String name, @NonNull String value) {
- return putString(name, value, false);
- }
-
- @Override
- public boolean putStringForUser(@NonNull String name, @NonNull String value, int userHandle) {
- return putStringForUser(name, value, null, false, userHandle, false);
- }
-
- @Override
- public boolean putStringForUser(@NonNull String name, @NonNull String value, String tag,
- boolean makeDefault, int userHandle, boolean overrideableByRestore) {
- SettingsKey key = new SettingsKey(userHandle, getUriFor(name).toString());
- mValues.put(key, value);
-
- Uri uri = getUriFor(name);
- for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
- observer.dispatchChange(false, List.of(uri), 0, userHandle);
- }
- for (ContentObserver observer :
- mContentObserversAllUsers.getOrDefault(uri.toString(), new ArrayList<>())) {
- observer.dispatchChange(false, List.of(uri), 0, userHandle);
- }
- return true;
- }
-
- @Override
- public boolean putString(@NonNull String name, @NonNull String value, @NonNull String tag,
- boolean makeDefault) {
- return putString(name, value);
- }
-
- private static class SettingsKey extends Pair<Integer, String> {
- SettingsKey(Integer first, String second) {
- super(first, second);
- }
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
new file mode 100644
index 0000000..e5d113b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.util.settings
+
+import android.annotation.UserIdInt
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.UserHandle
+import android.util.Pair
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.util.settings.SettingsProxy.CurrentUserIdProvider
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+
+class FakeSettings : SecureSettings, SystemSettings, UserSettingsProxy {
+ private val values = mutableMapOf<SettingsKey, String?>()
+ private val contentObservers = mutableMapOf<SettingsKey, MutableList<ContentObserver>>()
+ private val contentObserversAllUsers = mutableMapOf<String, MutableList<ContentObserver>>()
+
+ override val backgroundDispatcher: CoroutineDispatcher
+
+ @UserIdInt override var userId = UserHandle.USER_CURRENT
+ override val currentUserProvider: CurrentUserIdProvider
+
+ @Deprecated(
+ """Please use FakeSettings(testDispatcher) to provide the same dispatcher used
+ by main test scope."""
+ )
+ constructor() {
+ backgroundDispatcher = StandardTestDispatcher(scheduler = null, name = null)
+ currentUserProvider = CurrentUserIdProvider { userId }
+ }
+
+ constructor(dispatcher: CoroutineDispatcher) {
+ backgroundDispatcher = dispatcher
+ currentUserProvider = CurrentUserIdProvider { userId }
+ }
+
+ constructor(dispatcher: CoroutineDispatcher, currentUserProvider: CurrentUserIdProvider) {
+ backgroundDispatcher = dispatcher
+ this.currentUserProvider = currentUserProvider
+ }
+
+ @VisibleForTesting
+ internal constructor(initialKey: String, initialValue: String) : this() {
+ putString(initialKey, initialValue)
+ }
+
+ @VisibleForTesting
+ internal constructor(initialValues: Map<String, String>) : this() {
+ for ((key, value) in initialValues) {
+ putString(key, value)
+ }
+ }
+
+ override fun getContentResolver(): ContentResolver {
+ throw UnsupportedOperationException("FakeSettings.getContentResolver is not implemented")
+ }
+
+ override fun registerContentObserverForUserSync(
+ uri: Uri,
+ notifyForDescendants: Boolean,
+ settingsObserver: ContentObserver,
+ userHandle: Int
+ ) {
+ if (userHandle == UserHandle.USER_ALL) {
+ contentObserversAllUsers
+ .getOrPut(uri.toString()) { mutableListOf() }
+ .add(settingsObserver)
+ } else {
+ val key = SettingsKey(userHandle, uri.toString())
+ contentObservers.getOrPut(key) { mutableListOf() }.add(settingsObserver)
+ }
+ }
+
+ override fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
+ contentObservers.values.onEach { it.remove(settingsObserver) }
+ contentObserversAllUsers.values.onEach { it.remove(settingsObserver) }
+ }
+
+ override suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) =
+ suspendAdvanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserver(uri, settingsObserver)
+ }
+
+ override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver): Job =
+ advanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverAsync(uri, settingsObserver)
+ }
+
+ override suspend fun registerContentObserver(
+ uri: Uri,
+ notifyForDescendants: Boolean,
+ settingsObserver: ContentObserver
+ ) = suspendAdvanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserver(
+ uri,
+ notifyForDescendants,
+ settingsObserver
+ )
+ }
+
+ override fun registerContentObserverAsync(
+ uri: Uri,
+ notifyForDescendants: Boolean,
+ settingsObserver: ContentObserver
+ ): Job = advanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverAsync(
+ uri,
+ notifyForDescendants,
+ settingsObserver
+ )
+ }
+
+ override suspend fun registerContentObserverForUser(
+ name: String,
+ settingsObserver: ContentObserver,
+ userHandle: Int
+ ) = suspendAdvanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverForUser(name, settingsObserver, userHandle)
+ }
+
+ override fun registerContentObserverForUserAsync(
+ name: String,
+ settingsObserver: ContentObserver,
+ userHandle: Int
+ ): Job = advanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverForUserAsync(
+ name,
+ settingsObserver,
+ userHandle
+ )
+ }
+
+ override fun unregisterContentObserverAsync(settingsObserver: ContentObserver): Job =
+ advanceDispatcher {
+ super<UserSettingsProxy>.unregisterContentObserverAsync(settingsObserver)
+ }
+
+ override suspend fun registerContentObserverForUser(
+ uri: Uri,
+ settingsObserver: ContentObserver,
+ userHandle: Int
+ ) = suspendAdvanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverForUser(uri, settingsObserver, userHandle)
+ }
+
+ override fun registerContentObserverForUserAsync(
+ uri: Uri,
+ settingsObserver: ContentObserver,
+ userHandle: Int
+ ): Job = advanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverForUserAsync(
+ uri,
+ settingsObserver,
+ userHandle
+ )
+ }
+
+ override fun registerContentObserverForUserAsync(
+ uri: Uri,
+ settingsObserver: ContentObserver,
+ userHandle: Int,
+ registered: Runnable
+ ): Job = advanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverForUserAsync(
+ uri,
+ settingsObserver,
+ userHandle,
+ registered
+ )
+ }
+
+ override suspend fun registerContentObserverForUser(
+ name: String,
+ notifyForDescendants: Boolean,
+ settingsObserver: ContentObserver,
+ userHandle: Int
+ ) = suspendAdvanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverForUser(
+ name,
+ notifyForDescendants,
+ settingsObserver,
+ userHandle
+ )
+ }
+
+ override fun registerContentObserverForUserAsync(
+ name: String,
+ notifyForDescendants: Boolean,
+ settingsObserver: ContentObserver,
+ userHandle: Int
+ ) = advanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverForUserAsync(
+ name,
+ notifyForDescendants,
+ settingsObserver,
+ userHandle
+ )
+ }
+
+ override fun registerContentObserverForUserAsync(
+ uri: Uri,
+ notifyForDescendants: Boolean,
+ settingsObserver: ContentObserver,
+ userHandle: Int
+ ): Job = advanceDispatcher {
+ super<UserSettingsProxy>.registerContentObserverForUserAsync(
+ uri,
+ notifyForDescendants,
+ settingsObserver,
+ userHandle
+ )
+ }
+
+ override fun getUriFor(name: String): Uri {
+ return Uri.withAppendedPath(CONTENT_URI, name)
+ }
+
+ override fun getString(name: String): String? {
+ return getStringForUser(name, userId)
+ }
+
+ override fun getStringForUser(name: String, userHandle: Int): String? {
+ return values[SettingsKey(userHandle, getUriFor(name).toString())]
+ }
+
+ override fun putString(name: String, value: String?, overrideableByRestore: Boolean): Boolean {
+ return putStringForUser(name, value, null, false, userId, overrideableByRestore)
+ }
+
+ override fun putString(name: String, value: String?): Boolean {
+ return putString(name, value, false)
+ }
+
+ override fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean {
+ return putStringForUser(name, value, null, false, userHandle, false)
+ }
+
+ override fun putStringForUser(
+ name: String,
+ value: String?,
+ tag: String?,
+ makeDefault: Boolean,
+ userHandle: Int,
+ overrideableByRestore: Boolean
+ ): Boolean {
+ val key = SettingsKey(userHandle, getUriFor(name).toString())
+ values[key] = value
+ val uri = getUriFor(name)
+ contentObservers[key]?.onEach { it.dispatchChange(false, listOf(uri), 0, userHandle) }
+ contentObserversAllUsers[uri.toString()]?.onEach {
+ it.dispatchChange(false, listOf(uri), 0, userHandle)
+ }
+ return true
+ }
+
+ override fun putString(
+ name: String,
+ value: String?,
+ tag: String?,
+ makeDefault: Boolean
+ ): Boolean {
+ return putString(name, value)
+ }
+
+ /** Runs current jobs on dispatcher after calling the method. */
+ private fun <T> advanceDispatcher(f: () -> T): T {
+ val result = f()
+ testDispatcherRunCurrent()
+ return result
+ }
+
+ private suspend fun <T> suspendAdvanceDispatcher(f: suspend () -> T): T {
+ val result = f()
+ testDispatcherRunCurrent()
+ return result
+ }
+
+ private fun testDispatcherRunCurrent() {
+ val testDispatcher = backgroundDispatcher as? TestDispatcher
+ testDispatcher?.scheduler?.runCurrent()
+ }
+
+ private data class SettingsKey(val first: Int, val second: String) :
+ Pair<Int, String>(first, second)
+
+ companion object {
+ val CONTENT_URI = Uri.parse("content://settings/fake")
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
index 2ba1211..0b438d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.shared.volumePanelLogger
val Kosmos.volumePanelGlobalStateRepository by
- Kosmos.Fixture { VolumePanelGlobalStateRepository(dumpManager) }
+ Kosmos.Fixture { VolumePanelGlobalStateRepository(dumpManager, volumePanelLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
index a18f498..3804a9f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
@@ -28,6 +28,7 @@
import com.android.systemui.volume.panel.domain.defaultCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import com.android.systemui.volume.panel.ui.composable.enabledComponents
+import com.android.systemui.volume.shared.volumePanelLogger
import javax.inject.Provider
var Kosmos.criteriaByKey: Map<VolumePanelComponentKey, Provider<ComponentAvailabilityCriteria>> by
@@ -50,6 +51,7 @@
enabledComponents,
{ defaultCriteria },
testScope.backgroundScope,
+ volumePanelLogger,
criteriaByKey,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
index 34a008f..c4fb9e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
@@ -18,17 +18,19 @@
import android.content.applicationContext
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory
import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
+import com.android.systemui.volume.shared.volumePanelLogger
var Kosmos.volumePanelStartables: Set<VolumePanelStartable> by Kosmos.Fixture { emptySet() }
var Kosmos.volumePanelViewModel: VolumePanelViewModel by
- Kosmos.Fixture { volumePanelViewModelFactory.create(testScope.backgroundScope) }
+ Kosmos.Fixture { volumePanelViewModelFactory.create(applicationCoroutineScope) }
val Kosmos.volumePanelViewModelFactory: VolumePanelViewModel.Factory by
Kosmos.Fixture {
@@ -37,6 +39,8 @@
volumePanelComponentFactory,
configurationController,
broadcastDispatcher,
+ dumpManager,
+ volumePanelLogger,
volumePanelGlobalStateInteractor,
)
}
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 68f185e..cc9b70e 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -281,6 +281,12 @@
com.android.server.LocalServices
+com.android.internal.graphics.cam.Cam
+com.android.internal.graphics.cam.CamUtils
+com.android.internal.graphics.cam.Frame
+com.android.internal.graphics.cam.HctSolver
+com.android.internal.graphics.ColorUtils
+
com.android.internal.util.BitUtils
com.android.internal.util.BitwiseInputStream
com.android.internal.util.BitwiseOutputStream
diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
index 56da231..2ce5c2b 100644
--- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
@@ -78,6 +78,9 @@
private final AccessibilityManagerService mAms;
private final Handler mHandler;
+ /** Thread to wait for virtual mouse creation to complete */
+ private final Thread mCreateVirtualMouseThread;
+
VirtualDeviceManager.VirtualDevice mVirtualDevice = null;
private VirtualMouse mVirtualMouse = null;
@@ -154,34 +157,47 @@
mHandler = new Handler(looper, this);
// Create the virtual mouse on a separate thread since virtual device creation
// should happen on an auxiliary thread, and not from the handler's thread.
- // This is because virtual device creation is a blocking operation and can cause a
- // deadlock if it is called from the handler's thread.
- new Thread(() -> {
+ // This is because the handler thread is the same as the main thread,
+ // and the main thread will be blocked waiting for the virtual device to be created.
+ mCreateVirtualMouseThread = new Thread(() -> {
mVirtualMouse = createVirtualMouse(displayId);
- }).start();
+ });
+ mCreateVirtualMouseThread.start();
+ }
+ /**
+ * Wait for {@code mVirtualMouse} to be created.
+ * This will ensure that {@code mVirtualMouse} is always created before
+ * trying to send mouse events.
+ **/
+ private void waitForVirtualMouseCreation() {
+ try {
+ // Block the current thread until the virtual mouse creation thread completes.
+ mCreateVirtualMouseThread.join();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
}
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
private void sendVirtualMouseRelativeEvent(float x, float y) {
- if (mVirtualMouse != null) {
- mVirtualMouse.sendRelativeEvent(new VirtualMouseRelativeEvent.Builder()
- .setRelativeX(x)
- .setRelativeY(y)
- .build()
- );
- }
+ waitForVirtualMouseCreation();
+ mVirtualMouse.sendRelativeEvent(new VirtualMouseRelativeEvent.Builder()
+ .setRelativeX(x)
+ .setRelativeY(y)
+ .build()
+ );
}
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
private void sendVirtualMouseButtonEvent(int buttonCode, int actionCode) {
- if (mVirtualMouse != null) {
- mVirtualMouse.sendButtonEvent(new VirtualMouseButtonEvent.Builder()
- .setAction(actionCode)
- .setButtonCode(buttonCode)
- .build()
- );
- }
+ waitForVirtualMouseCreation();
+ mVirtualMouse.sendButtonEvent(new VirtualMouseButtonEvent.Builder()
+ .setAction(actionCode)
+ .setButtonCode(buttonCode)
+ .build()
+ );
}
/**
@@ -205,12 +221,11 @@
case DOWN_MOVE_OR_SCROLL -> -1.0f;
default -> 0.0f;
};
- if (mVirtualMouse != null) {
- mVirtualMouse.sendScrollEvent(new VirtualMouseScrollEvent.Builder()
- .setYAxisMovement(y)
- .build()
- );
- }
+ waitForVirtualMouseCreation();
+ mVirtualMouse.sendScrollEvent(new VirtualMouseScrollEvent.Builder()
+ .setYAxisMovement(y)
+ .build()
+ );
if (DEBUG) {
Slog.d(LOG_TAG, "Performed mouse key event: " + mouseKeyEvent.name()
+ " for scroll action with axis movement (y=" + y + ")");
diff --git a/services/appfunctions/OWNERS b/services/appfunctions/OWNERS
new file mode 100644
index 0000000..b310894
--- /dev/null
+++ b/services/appfunctions/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/appfunctions/OWNERS
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 0fdd57d..dca1491 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -264,4 +264,11 @@
}
});
}
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
+ @Override
+ public int getSensorId() {
+ super.getSensorId_enforcePermission();
+ return mSensorId;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 8dc560b..caa2c1c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -293,4 +293,11 @@
}
});
}
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
+ @Override
+ public int getSensorId() {
+ super.getSensorId_enforcePermission();
+ return mSensorId;
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 4dbbfa2..c77b768 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -512,6 +512,9 @@
/**
* Gets the next input method and subtype from the given ones.
*
+ * <p>If the given input method and subtype are not found, this returns the most recent
+ * input method and subtype.</p>
+ *
* @param imi the input method to find the next value from.
* @param subtype the input method subtype to find the next value from, if any.
* @param onlyCurrentIme whether to consider only subtypes of the current input method.
@@ -523,17 +526,20 @@
public ImeSubtypeListItem next(@NonNull InputMethodInfo imi,
@Nullable InputMethodSubtype subtype, boolean onlyCurrentIme,
boolean useRecency, boolean forward) {
- final int size = mItems.size();
- if (size <= 1) {
+ if (mItems.isEmpty()) {
return null;
}
final int index = getIndex(imi, subtype, useRecency);
if (index < 0) {
- return null;
+ Slog.w(TAG, "Trying to switch away from input method: " + imi
+ + " and subtype " + subtype + " which are not in the list,"
+ + " falling back to most recent item in list.");
+ return mItems.get(mRecencyMap[0]);
}
final int incrementSign = (forward ? 1 : -1);
+ final int size = mItems.size();
for (int i = 1; i < size; i++) {
final int nextIndex = (index + i * incrementSign + size) % size;
final int mappedIndex = useRecency ? mRecencyMap[nextIndex] : nextIndex;
@@ -554,7 +560,7 @@
*/
public boolean setMostRecent(@NonNull InputMethodInfo imi,
@Nullable InputMethodSubtype subtype) {
- if (mItems.size() <= 1) {
+ if (mItems.isEmpty()) {
return false;
}
@@ -849,6 +855,9 @@
/**
* Gets the next input method and subtype, starting from the given ones, in the given direction.
*
+ * <p>If the given input method and subtype are not found, this returns the most recent
+ * input method and subtype.</p>
+ *
* @param onlyCurrentIme whether to consider only subtypes of the current input method.
* @param imi the input method to find the next value from.
* @param subtype the input method subtype to find the next value from, if any.
@@ -867,6 +876,9 @@
* Gets the next input method and subtype suitable for hardware keyboards, starting from the
* given ones, in the given direction.
*
+ * <p>If the given input method and subtype are not found, this returns the most recent
+ * input method and subtype.</p>
+ *
* @param onlyCurrentIme whether to consider only subtypes of the current input method.
* @param imi the input method to find the next value from.
* @param subtype the input method subtype to find the next value from, if any.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 17f6561..a6f4c0e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -530,6 +530,12 @@
*/
private boolean mUseDifferentDelaysForBackgroundChain;
+ /**
+ * Core uids and apps without the internet permission will not have any firewall rules applied
+ * to them.
+ */
+ private boolean mNeverApplyRulesToCoreUids;
+
// See main javadoc for instructions on how to use these locks.
final Object mUidRulesFirstLock = new Object();
final Object mNetworkPoliciesSecondLock = new Object();
@@ -760,7 +766,8 @@
/** List of apps indexed by uid and whether they have the internet permission */
@GuardedBy("mUidRulesFirstLock")
- private final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
+ @VisibleForTesting
+ final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
/**
* Map of uid -> UidStateCallbackInfo objects holding the data received from
@@ -1038,6 +1045,7 @@
mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
mUseDifferentDelaysForBackgroundChain = Flags.useDifferentDelaysForBackgroundChain();
+ mNeverApplyRulesToCoreUids = Flags.neverApplyRulesToCoreUids();
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
@@ -4088,6 +4096,8 @@
+ mUseMeteredFirewallChains);
fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": "
+ mUseDifferentDelaysForBackgroundChain);
+ fout.println(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS + ": "
+ + mNeverApplyRulesToCoreUids);
fout.println();
fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -4878,6 +4888,12 @@
int[] idleUids = mUsageStats.getIdleUidsForUser(user.id);
for (int uid : idleUids) {
if (!mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid), false)) {
+ if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+ // This check is needed to keep mUidFirewallStandbyRules free of any
+ // such uids. Doing this keeps it in sync with the actual rules applied
+ // in the underlying connectivity stack.
+ continue;
+ }
// quick check: if this uid doesn't have INTERNET permission, it
// doesn't have network access anyway, so it is a waste to mess
// with it here.
@@ -5180,6 +5196,11 @@
@GuardedBy("mUidRulesFirstLock")
private boolean isUidValidForAllowlistRulesUL(int uid) {
+ return isUidValidForRulesUL(uid);
+ }
+
+ @GuardedBy("mUidRulesFirstLock")
+ private boolean isUidValidForRulesUL(int uid) {
return UserHandle.isApp(uid) && hasInternetPermissionUL(uid);
}
@@ -6194,41 +6215,33 @@
}
}
- private void addSdkSandboxUidsIfNeeded(SparseIntArray uidRules) {
- final int size = uidRules.size();
- final SparseIntArray sdkSandboxUids = new SparseIntArray();
- for (int index = 0; index < size; index++) {
- final int uid = uidRules.keyAt(index);
- final int rule = uidRules.valueAt(index);
- if (Process.isApplicationUid(uid)) {
- sdkSandboxUids.put(Process.toSdkSandboxUid(uid), rule);
- }
- }
-
- for (int index = 0; index < sdkSandboxUids.size(); index++) {
- final int uid = sdkSandboxUids.keyAt(index);
- final int rule = sdkSandboxUids.valueAt(index);
- uidRules.put(uid, rule);
- }
- }
-
/**
* Set uid rules on a particular firewall chain. This is going to synchronize the rules given
* here to netd. It will clean up dead rules and make sure the target chain only contains rules
* specified here.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
- addSdkSandboxUidsIfNeeded(uidRules);
try {
int size = uidRules.size();
- int[] uids = new int[size];
- int[] rules = new int[size];
+ final IntArray uids = new IntArray(size);
+ final IntArray rules = new IntArray(size);
for(int index = size - 1; index >= 0; --index) {
- uids[index] = uidRules.keyAt(index);
- rules[index] = uidRules.valueAt(index);
+ final int uid = uidRules.keyAt(index);
+ if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+ continue;
+ }
+ uids.add(uid);
+ rules.add(uidRules.valueAt(index));
+ if (Process.isApplicationUid(uid)) {
+ uids.add(Process.toSdkSandboxUid(uid));
+ rules.add(uidRules.valueAt(index));
+ }
}
- mNetworkManager.setFirewallUidRules(chain, uids, rules);
- mLogger.firewallRulesChanged(chain, uids, rules);
+ final int[] uidArray = uids.toArray();
+ final int[] ruleArray = rules.toArray();
+ mNetworkManager.setFirewallUidRules(chain, uidArray, ruleArray);
+ mLogger.firewallRulesChanged(chain, uidArray, ruleArray);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
@@ -6241,6 +6254,9 @@
*/
@GuardedBy("mUidRulesFirstLock")
private void setUidFirewallRuleUL(int chain, int uid, int rule) {
+ if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+ return;
+ }
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
"setUidFirewallRuleUL: " + chain + "/" + uid + "/" + rule);
@@ -6249,8 +6265,6 @@
if (chain == FIREWALL_CHAIN_STANDBY) {
mUidFirewallStandbyRules.put(uid, rule);
}
- // Note that we do not need keep a separate cache of uid rules for chains that we do
- // not call #setUidFirewallRulesUL for.
try {
mNetworkManager.setFirewallUidRule(chain, uid, rule);
@@ -6295,6 +6309,8 @@
* Resets all firewall rules associated with an UID.
*/
private void resetUidFirewallRules(int uid) {
+ // Resetting rules for uids with isUidValidForRulesUL = false should be OK as no rules
+ // should be previously set and the downstream code will skip no-op changes.
try {
mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid,
FIREWALL_RULE_DEFAULT);
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index 586baf0..7f04e66 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -27,3 +27,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "never_apply_rules_to_core_uids"
+ namespace: "backstage_power"
+ description: "Removes all rule bookkeeping and evaluation logic for core uids and uids without the internet permission"
+ bug: "356956588"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index a44e553..66e61c0 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,9 +16,6 @@
package com.android.server.notification;
-import static android.service.notification.Condition.STATE_TRUE;
-import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
-
import android.app.INotificationManager;
import android.app.NotificationManager;
import android.content.ComponentName;
@@ -322,20 +319,7 @@
final Condition c = conditions[i];
final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
r.info = info;
- if (android.app.Flags.modesUi()) {
- // if user turned on the mode, ignore the update unless the app also wants the
- // mode on. this will update the origin of the mode and let the owner turn it
- // off when the context ends
- if (r.condition != null && r.condition.source == ORIGIN_USER_IN_SYSTEMUI) {
- if (r.condition.state == STATE_TRUE && c.state == STATE_TRUE) {
- r.condition = c;
- }
- } else {
- r.condition = c;
- }
- } else {
- r.condition = c;
- }
+ r.condition = c;
}
}
final int N = conditions.length;
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index b4459cb..9818916 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -1519,7 +1519,14 @@
@Override
public void setLastNotificationUpdateTimeMs(NotificationRecord record,
long timestampMillis) {
- super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+ if (Flags.politeNotificationsAttnUpdate()) {
+ // Set last update per package/channel only for exempt notifications
+ if (isAvalancheExempted(record)) {
+ super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+ }
+ } else {
+ super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+ }
mLastNotificationTimestamp = timestampMillis;
mAppStrategy.setLastNotificationUpdateTimeMs(record, timestampMillis);
}
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 268d835..d495ef5 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -82,7 +82,7 @@
for (ZenRule automaticRule : config.automaticRules.values()) {
if (automaticRule.component != null) {
evaluateRule(automaticRule, current, trigger, processSubscriptions, false);
- updateSnoozing(automaticRule);
+ automaticRule.reconsiderConditionOverride();
}
}
@@ -187,13 +187,4 @@
+ rule.conditionId);
}
}
-
- private boolean updateSnoozing(ZenRule rule) {
- if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
- rule.snoozing = false;
- if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
- return true;
- }
- return false;
- }
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8c280ed..db48835 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -37,6 +37,8 @@
import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_APP;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_ACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.Preconditions.checkArgument;
@@ -643,10 +645,10 @@
if ((rule.userModifiedFields & AutomaticZenRule.FIELD_INTERRUPTION_FILTER) == 0) {
rule.zenMode = zenMode;
}
- rule.snoozing = false;
rule.condition = new Condition(rule.conditionId,
mContext.getString(R.string.zen_mode_implicit_activated),
STATE_TRUE);
+ rule.resetConditionOverride();
setConfigLocked(newConfig, /* triggeringComponent= */ null, ORIGIN_APP,
"applyGlobalZenModeAsImplicitZenRule", callingUid);
@@ -867,8 +869,8 @@
ZenRule deletedRule = ruleToRemove.copy();
deletedRule.deletionInstant = Instant.now(mClock);
// If the rule is restored it shouldn't be active (or snoozed).
- deletedRule.snoozing = false;
deletedRule.condition = null;
+ deletedRule.resetConditionOverride();
// Overwrites a previously-deleted rule with the same conditionId, but that's okay.
config.deletedRules.put(deletedKey, deletedRule);
}
@@ -885,7 +887,12 @@
if (rule == null || !canManageAutomaticZenRule(rule)) {
return Condition.STATE_UNKNOWN;
}
- return rule.condition != null ? rule.condition.state : STATE_FALSE;
+ if (Flags.modesApi() && Flags.modesUi()) {
+ return rule.isAutomaticActive() ? STATE_TRUE : STATE_FALSE;
+ } else {
+ // Buggy, does not consider snoozing!
+ return rule.condition != null ? rule.condition.state : STATE_FALSE;
+ }
}
}
@@ -943,12 +950,40 @@
}
for (ZenRule rule : rules) {
- rule.condition = condition;
- updateSnoozing(rule);
+ applyConditionAndReconsiderOverride(rule, condition, origin);
setConfigLocked(config, rule.component, origin, "conditionChanged", callingUid);
}
}
+ private static void applyConditionAndReconsiderOverride(ZenRule rule, Condition condition,
+ int origin) {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ if (origin == ORIGIN_USER_IN_SYSTEMUI && condition != null
+ && condition.source == SOURCE_USER_ACTION) {
+ // Apply as override, instead of actual condition.
+ if (condition.state == STATE_TRUE) {
+ // Manually turn on a rule -> Apply override.
+ rule.setConditionOverride(OVERRIDE_ACTIVATE);
+ } else if (condition.state == STATE_FALSE) {
+ // Manually turn off a rule. If the rule was manually activated before, reset
+ // override -- but only if this will not result in the rule turning on
+ // immediately because of a previously snoozed condition! In that case, apply
+ // deactivate-override.
+ rule.resetConditionOverride();
+ if (rule.isAutomaticActive()) {
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
+ }
+ }
+ } else {
+ rule.condition = condition;
+ rule.reconsiderConditionOverride();
+ }
+ } else {
+ rule.condition = condition;
+ rule.reconsiderConditionOverride();
+ }
+ }
+
private static List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id,
Condition condition) {
List<ZenRule> matchingRules = new ArrayList<>();
@@ -971,15 +1006,6 @@
return true;
}
- private boolean updateSnoozing(ZenRule rule) {
- if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
- rule.snoozing = false;
- if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
- return true;
- }
- return false;
- }
-
public int getCurrentInstanceCount(ComponentName cn) {
if (cn == null) {
return 0;
@@ -1181,7 +1207,7 @@
if (rule.enabled != azr.isEnabled()) {
rule.enabled = azr.isEnabled();
- rule.snoozing = false;
+ rule.resetConditionOverride();
modified = true;
}
if (!Objects.equals(rule.configurationActivity, azr.getConfigurationActivity())) {
@@ -1271,7 +1297,7 @@
return modified;
} else {
if (rule.enabled != azr.isEnabled()) {
- rule.snoozing = false;
+ rule.resetConditionOverride();
}
rule.name = azr.getName();
rule.condition = null;
@@ -1573,18 +1599,16 @@
// For API calls (different origin) keep old behavior of snoozing all rules.
for (ZenRule automaticRule : newConfig.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
- automaticRule.snoozing = true;
+ automaticRule.setConditionOverride(OVERRIDE_DEACTIVATE);
}
}
}
} else {
if (zenMode == Global.ZEN_MODE_OFF) {
newConfig.manualRule = null;
- // User deactivation of DND means just turning off the manual DND rule.
- // For API calls (different origin) keep old behavior of snoozing all rules.
for (ZenRule automaticRule : newConfig.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
- automaticRule.snoozing = true;
+ automaticRule.setConditionOverride(OVERRIDE_DEACTIVATE);
}
}
@@ -1626,13 +1650,11 @@
void dump(ProtoOutputStream proto) {
proto.write(ZenModeProto.ZEN_MODE, mZenMode);
synchronized (mConfigLock) {
- if (mConfig.manualRule != null) {
+ if (mConfig.isManualActive()) {
mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
for (ZenRule rule : mConfig.automaticRules.values()) {
- if (rule.enabled && rule.condition != null
- && rule.condition.state == STATE_TRUE
- && !rule.snoozing) {
+ if (rule.isAutomaticActive()) {
rule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
}
@@ -1695,8 +1717,8 @@
for (ZenRule automaticRule : config.automaticRules.values()) {
if (forRestore) {
// don't restore transient state from restored automatic rules
- automaticRule.snoozing = false;
automaticRule.condition = null;
+ automaticRule.resetConditionOverride();
automaticRule.creationTime = time;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 46585a5..6303ecd 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -80,6 +80,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.KeepForWeakReference;
import com.android.internal.content.PackageMonitor;
import com.android.internal.content.om.OverlayConfig;
@@ -1180,6 +1181,7 @@
// intent, querying the PackageManagerService for the actual current
// state may lead to contradictions within OMS. Better then to lag
// behind until all pending intents have been processed.
+ @GuardedBy("itself")
private final ArrayMap<String, PackageStateUsers> mCache = new ArrayMap<>();
private final ArraySet<Integer> mInitializedUsers = new ArraySet<>();
@@ -1207,10 +1209,12 @@
}
final ArrayMap<String, PackageState> userPackages = new ArrayMap<>();
- for (int i = 0, n = mCache.size(); i < n; i++) {
- final PackageStateUsers pkg = mCache.valueAt(i);
- if (pkg.mInstalledUsers.contains(userId)) {
- userPackages.put(mCache.keyAt(i), pkg.mPackageState);
+ synchronized (mCache) {
+ for (int i = 0, n = mCache.size(); i < n; i++) {
+ final PackageStateUsers pkg = mCache.valueAt(i);
+ if (pkg.mInstalledUsers.contains(userId)) {
+ userPackages.put(mCache.keyAt(i), pkg.mPackageState);
+ }
}
}
return userPackages;
@@ -1220,7 +1224,11 @@
@Nullable
public PackageState getPackageStateForUser(@NonNull final String packageName,
final int userId) {
- final PackageStateUsers pkg = mCache.get(packageName);
+ final PackageStateUsers pkg;
+
+ synchronized (mCache) {
+ pkg = mCache.get(packageName);
+ }
if (pkg != null && pkg.mInstalledUsers.contains(userId)) {
return pkg.mPackageState;
}
@@ -1251,12 +1259,15 @@
@NonNull
private PackageState addPackageUser(@NonNull final PackageState pkg,
final int user) {
- PackageStateUsers pkgUsers = mCache.get(pkg.getPackageName());
- if (pkgUsers == null) {
- pkgUsers = new PackageStateUsers(pkg);
- mCache.put(pkg.getPackageName(), pkgUsers);
- } else {
- pkgUsers.mPackageState = pkg;
+ PackageStateUsers pkgUsers;
+ synchronized (mCache) {
+ pkgUsers = mCache.get(pkg.getPackageName());
+ if (pkgUsers == null) {
+ pkgUsers = new PackageStateUsers(pkg);
+ mCache.put(pkg.getPackageName(), pkgUsers);
+ } else {
+ pkgUsers.mPackageState = pkg;
+ }
}
pkgUsers.mInstalledUsers.add(user);
return pkgUsers.mPackageState;
@@ -1265,18 +1276,24 @@
@NonNull
private void removePackageUser(@NonNull final String packageName, final int user) {
- final PackageStateUsers pkgUsers = mCache.get(packageName);
- if (pkgUsers == null) {
- return;
+ // synchronize should include the call to the other removePackageUser() method so that
+ // the access and modification happen under the same lock.
+ synchronized (mCache) {
+ final PackageStateUsers pkgUsers = mCache.get(packageName);
+ if (pkgUsers == null) {
+ return;
+ }
+ removePackageUser(pkgUsers, user);
}
- removePackageUser(pkgUsers, user);
}
@NonNull
private void removePackageUser(@NonNull final PackageStateUsers pkg, final int user) {
pkg.mInstalledUsers.remove(user);
if (pkg.mInstalledUsers.isEmpty()) {
- mCache.remove(pkg.mPackageState.getPackageName());
+ synchronized (mCache) {
+ mCache.remove(pkg.mPackageState.getPackageName());
+ }
}
}
@@ -1386,8 +1403,10 @@
public void forgetAllPackageInfos(final int userId) {
// Iterate in reverse order since removing the package in all users will remove the
// package from the cache.
- for (int i = mCache.size() - 1; i >= 0; i--) {
- removePackageUser(mCache.valueAt(i), userId);
+ synchronized (mCache) {
+ for (int i = mCache.size() - 1; i >= 0; i--) {
+ removePackageUser(mCache.valueAt(i), userId);
+ }
}
}
@@ -1405,22 +1424,23 @@
public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
pw.println("AndroidPackage cache");
+ synchronized (mCache) {
+ if (!dumpState.isVerbose()) {
+ pw.println(TAB1 + mCache.size() + " package(s)");
+ return;
+ }
- if (!dumpState.isVerbose()) {
- pw.println(TAB1 + mCache.size() + " package(s)");
- return;
- }
+ if (mCache.size() == 0) {
+ pw.println(TAB1 + "<empty>");
+ return;
+ }
- if (mCache.size() == 0) {
- pw.println(TAB1 + "<empty>");
- return;
- }
-
- for (int i = 0, n = mCache.size(); i < n; i++) {
- final String packageName = mCache.keyAt(i);
- final PackageStateUsers pkg = mCache.valueAt(i);
- pw.print(TAB1 + packageName + ": " + pkg.mPackageState + " users=");
- pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
+ for (int i = 0, n = mCache.size(); i < n; i++) {
+ final String packageName = mCache.keyAt(i);
+ final PackageStateUsers pkg = mCache.valueAt(i);
+ pw.print(TAB1 + packageName + ": " + pkg.mPackageState + " users=");
+ pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 829ee27..57d7d79 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2105,6 +2105,10 @@
@Override
public void setUserAdmin(@UserIdInt int userId) {
checkManageUserAndAcrossUsersFullPermission("set user admin");
+ if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+ checkAdminStatusChangeAllowed(userId);
+ }
+
mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_GRANT_ADMIN);
UserData user;
synchronized (mPackagesLock) {
@@ -2133,6 +2137,10 @@
@Override
public void revokeUserAdmin(@UserIdInt int userId) {
checkManageUserAndAcrossUsersFullPermission("revoke admin privileges");
+ if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+ checkAdminStatusChangeAllowed(userId);
+ }
+
mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_REVOKE_ADMIN);
UserData user;
synchronized (mPackagesLock) {
@@ -4065,6 +4073,26 @@
}
}
+ /**
+ * Checks if changing the admin status of a target user is restricted
+ * due to the DISALLOW_GRANT_ADMIN restriction. If either the calling
+ * user or the target user has this restriction, a SecurityException
+ * is thrown.
+ *
+ * @param targetUser The user ID of the user whose admin status is being
+ * considered for change.
+ * @throws SecurityException if the admin status change is restricted due
+ * to the DISALLOW_GRANT_ADMIN restriction.
+ */
+ private void checkAdminStatusChangeAllowed(int targetUser) {
+ if (hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, UserHandle.getCallingUserId())
+ || hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, targetUser)) {
+ throw new SecurityException(
+ "Admin status change is restricted. The DISALLOW_GRANT_ADMIN "
+ + "restriction is applied either on the current or the target user.");
+ }
+ }
+
@GuardedBy({"mPackagesLock"})
private void writeBitmapLP(UserInfo info, Bitmap bitmap) {
try {
@@ -5443,6 +5471,13 @@
enforceUserRestriction(restriction, UserHandle.getCallingUserId(),
"Cannot add user");
+ if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+ if ((flags & UserInfo.FLAG_ADMIN) != 0) {
+ enforceUserRestriction(UserManager.DISALLOW_GRANT_ADMIN,
+ UserHandle.getCallingUserId(), "Cannot create ADMIN user");
+ }
+ }
+
return createUserInternalUnchecked(name, userType, flags, parentId,
/* preCreate= */ false, disallowedPackages, /* token= */ null);
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 3933759..a74c4e0 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -17,7 +17,6 @@
package com.android.server.vibrator;
import android.annotation.NonNull;
-import android.content.Context;
import android.hardware.vibrator.V1_0.EffectStrength;
import android.os.ExternalVibrationScale;
import android.os.VibrationAttributes;
@@ -25,6 +24,7 @@
import android.os.Vibrator;
import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.VibrationConfig;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
@@ -37,8 +37,11 @@
final class VibrationScaler {
private static final String TAG = "VibrationScaler";
+ // TODO(b/345186129): remove this once we finish migrating to scale factor and clean up flags.
// Scale levels. Each level, except MUTE, is defined as the delta between the current setting
// and the default intensity for that type of vibration (i.e. current - default).
+ // It's important that we apply the scaling on the delta between the two so
+ // that the default intensity level applies no scaling to application provided effects.
static final int SCALE_VERY_LOW = ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
@@ -53,35 +56,15 @@
private static final float SCALE_FACTOR_HIGH = 1.2f;
private static final float SCALE_FACTOR_VERY_HIGH = 1.4f;
- private static final ScaleLevel SCALE_LEVEL_NONE = new ScaleLevel(SCALE_FACTOR_NONE);
-
- // A mapping from the intensity adjustment to the scaling to apply, where the intensity
- // adjustment is defined as the delta between the default intensity level and the user selected
- // intensity level. It's important that we apply the scaling on the delta between the two so
- // that the default intensity level applies no scaling to application provided effects.
- private final SparseArray<ScaleLevel> mScaleLevels;
private final VibrationSettings mSettingsController;
private final int mDefaultVibrationAmplitude;
+ private final float mDefaultVibrationScaleLevelGain;
private final SparseArray<Float> mAdaptiveHapticsScales = new SparseArray<>();
- VibrationScaler(Context context, VibrationSettings settingsController) {
+ VibrationScaler(VibrationConfig config, VibrationSettings settingsController) {
mSettingsController = settingsController;
- mDefaultVibrationAmplitude = context.getResources().getInteger(
- com.android.internal.R.integer.config_defaultVibrationAmplitude);
-
- mScaleLevels = new SparseArray<>();
- mScaleLevels.put(SCALE_VERY_LOW, new ScaleLevel(SCALE_FACTOR_VERY_LOW));
- mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_FACTOR_LOW));
- mScaleLevels.put(SCALE_NONE, SCALE_LEVEL_NONE);
- mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH));
- mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH));
- }
-
- /**
- * Returns the default vibration amplitude configured for this device, value in [1,255].
- */
- public int getDefaultVibrationAmplitude() {
- return mDefaultVibrationAmplitude;
+ mDefaultVibrationAmplitude = config.getDefaultVibrationAmplitude();
+ mDefaultVibrationScaleLevelGain = config.getDefaultVibrationScaleLevelGain();
}
/**
@@ -111,6 +94,16 @@
}
/**
+ * Calculates the scale factor to be applied to a vibration with given usage.
+ *
+ * @param usageHint one of VibrationAttributes.USAGE_*
+ * @return The scale factor.
+ */
+ public float getScaleFactor(int usageHint) {
+ return scaleLevelToScaleFactor(getScaleLevel(usageHint));
+ }
+
+ /**
* Returns the adaptive haptics scale that should be applied to the vibrations with
* the given usage. When no adaptive scales are available for the usages, then returns 1
* indicating no scaling will be applied
@@ -135,20 +128,12 @@
@NonNull
public VibrationEffect scale(@NonNull VibrationEffect effect, int usageHint) {
int newEffectStrength = getEffectStrength(usageHint);
- ScaleLevel scaleLevel = mScaleLevels.get(getScaleLevel(usageHint));
+ float scaleFactor = getScaleFactor(usageHint);
float adaptiveScale = getAdaptiveHapticsScale(usageHint);
- if (scaleLevel == null) {
- // Something about our scaling has gone wrong, so just play with no scaling.
- Slog.e(TAG, "No configured scaling level found! (current="
- + mSettingsController.getCurrentIntensity(usageHint) + ", default= "
- + mSettingsController.getDefaultIntensity(usageHint) + ")");
- scaleLevel = SCALE_LEVEL_NONE;
- }
-
return effect.resolve(mDefaultVibrationAmplitude)
.applyEffectStrength(newEffectStrength)
- .scale(scaleLevel.factor)
+ .scale(scaleFactor)
.scaleLinearly(adaptiveScale);
}
@@ -192,14 +177,11 @@
void dump(IndentingPrintWriter pw) {
pw.println("VibrationScaler:");
pw.increaseIndent();
- pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude);
pw.println("ScaleLevels:");
pw.increaseIndent();
- for (int i = 0; i < mScaleLevels.size(); i++) {
- int scaleLevelKey = mScaleLevels.keyAt(i);
- ScaleLevel scaleLevel = mScaleLevels.valueAt(i);
- pw.println(scaleLevelToString(scaleLevelKey) + " = " + scaleLevel);
+ for (int level = SCALE_VERY_LOW; level <= SCALE_VERY_HIGH; level++) {
+ pw.println(scaleLevelToString(level) + " = " + scaleLevelToScaleFactor(level));
}
pw.decreaseIndent();
@@ -224,16 +206,24 @@
@Override
public String toString() {
+ StringBuilder scaleLevelsStr = new StringBuilder("{");
+ for (int level = SCALE_VERY_LOW; level <= SCALE_VERY_HIGH; level++) {
+ scaleLevelsStr.append(scaleLevelToString(level))
+ .append("=").append(scaleLevelToScaleFactor(level));
+ if (level < SCALE_FACTOR_VERY_HIGH) {
+ scaleLevelsStr.append(", ");
+ }
+ }
+ scaleLevelsStr.append("}");
+
return "VibrationScaler{"
- + "mScaleLevels=" + mScaleLevels
- + ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude
+ + "mScaleLevels=" + scaleLevelsStr
+ ", mAdaptiveHapticsScales=" + mAdaptiveHapticsScales
+ '}';
}
private int getEffectStrength(int usageHint) {
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
-
if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
// Bypassing user settings, or it has changed between checking and scaling. Use default.
currentIntensity = mSettingsController.getDefaultIntensity(usageHint);
@@ -244,17 +234,44 @@
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
private static int intensityToEffectStrength(int intensity) {
- switch (intensity) {
- case Vibrator.VIBRATION_INTENSITY_LOW:
- return EffectStrength.LIGHT;
- case Vibrator.VIBRATION_INTENSITY_MEDIUM:
- return EffectStrength.MEDIUM;
- case Vibrator.VIBRATION_INTENSITY_HIGH:
- return EffectStrength.STRONG;
- default:
+ return switch (intensity) {
+ case Vibrator.VIBRATION_INTENSITY_LOW -> EffectStrength.LIGHT;
+ case Vibrator.VIBRATION_INTENSITY_MEDIUM -> EffectStrength.MEDIUM;
+ case Vibrator.VIBRATION_INTENSITY_HIGH -> EffectStrength.STRONG;
+ default -> {
Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
- return EffectStrength.STRONG;
+ yield EffectStrength.STRONG;
+ }
+ };
+ }
+
+ /** Mapping of ExternalVibrationScale.ScaleLevel.SCALE_* values to scale factor. */
+ private float scaleLevelToScaleFactor(int level) {
+ if (Flags.hapticsScaleV2Enabled()) {
+ if (level == SCALE_NONE || level < SCALE_VERY_LOW || level > SCALE_VERY_HIGH) {
+ // Scale set to none or to a bad value, use default factor for no scaling.
+ return SCALE_FACTOR_NONE;
+ }
+ float scaleFactor = (float) Math.pow(mDefaultVibrationScaleLevelGain, level);
+ if (scaleFactor <= 0) {
+ // Something about our scaling has gone wrong, so just play with no scaling.
+ Slog.wtf(TAG, String.format(Locale.ROOT, "Error in scaling calculations, ended up"
+ + " with invalid scale factor %.2f for scale level %s and default"
+ + " level gain of %.2f", scaleFactor, scaleLevelToString(level),
+ mDefaultVibrationScaleLevelGain));
+ scaleFactor = SCALE_FACTOR_NONE;
+ }
+ return scaleFactor;
}
+
+ return switch (level) {
+ case SCALE_VERY_LOW -> SCALE_FACTOR_VERY_LOW;
+ case SCALE_LOW -> SCALE_FACTOR_LOW;
+ case SCALE_HIGH -> SCALE_FACTOR_HIGH;
+ case SCALE_VERY_HIGH -> SCALE_FACTOR_VERY_HIGH;
+ // Scale set to none or to a bad value, use default factor for no scaling.
+ default -> SCALE_FACTOR_NONE;
+ };
}
static String scaleLevelToString(int scaleLevel) {
@@ -267,18 +284,4 @@
default -> String.valueOf(scaleLevel);
};
}
-
- /** Represents the scale that must be applied to a vibration effect intensity. */
- private static final class ScaleLevel {
- public final float factor;
-
- ScaleLevel(float factor) {
- this.factor = factor;
- }
-
- @Override
- public String toString() {
- return "ScaleLevel{factor=" + factor + "}";
- }
- }
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index f2f5eda..0d6778c 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,7 +16,6 @@
package com.android.server.vibrator;
-import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
import static android.os.VibrationAttributes.USAGE_ALARM;
import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
@@ -191,8 +190,6 @@
@GuardedBy("mLock")
private boolean mVibrateOn;
@GuardedBy("mLock")
- private boolean mKeyboardVibrationOn;
- @GuardedBy("mLock")
private int mRingerMode;
@GuardedBy("mLock")
private boolean mOnWirelessCharger;
@@ -532,14 +529,6 @@
return false;
}
- if (mVibrationConfig.isKeyboardVibrationSettingsSupported()) {
- int category = callerInfo.attrs.getCategory();
- if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {
- // Keyboard touch has a different user setting.
- return mKeyboardVibrationOn;
- }
- }
-
// Apply individual user setting based on usage.
return getCurrentIntensity(usage) != Vibrator.VIBRATION_INTENSITY_OFF;
}
@@ -556,10 +545,11 @@
mVibrateInputDevices =
loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0, userHandle) > 0;
mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1, userHandle) > 0;
- mKeyboardVibrationOn = loadSystemSetting(
- Settings.System.KEYBOARD_VIBRATION_ENABLED, 1, userHandle) > 0;
- int keyboardIntensity = getDefaultIntensity(USAGE_IME_FEEDBACK);
+ boolean isKeyboardVibrationOn = loadSystemSetting(
+ Settings.System.KEYBOARD_VIBRATION_ENABLED, 1, userHandle) > 0;
+ int keyboardIntensity = toIntensity(isKeyboardVibrationOn,
+ getDefaultIntensity(USAGE_IME_FEEDBACK));
int alarmIntensity = toIntensity(
loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1, userHandle),
getDefaultIntensity(USAGE_ALARM));
@@ -654,7 +644,6 @@
return "VibrationSettings{"
+ "mVibratorConfig=" + mVibrationConfig
+ ", mVibrateOn=" + mVibrateOn
- + ", mKeyboardVibrationOn=" + mKeyboardVibrationOn
+ ", mVibrateInputDevices=" + mVibrateInputDevices
+ ", mBatterySaverMode=" + mBatterySaverMode
+ ", mRingerMode=" + ringerModeToString(mRingerMode)
@@ -671,7 +660,6 @@
pw.println("VibrationSettings:");
pw.increaseIndent();
pw.println("vibrateOn = " + mVibrateOn);
- pw.println("keyboardVibrationOn = " + mKeyboardVibrationOn);
pw.println("vibrateInputDevices = " + mVibrateInputDevices);
pw.println("batterySaverMode = " + mBatterySaverMode);
pw.println("ringerMode = " + ringerModeToString(mRingerMode));
@@ -698,8 +686,6 @@
void dump(ProtoOutputStream proto) {
synchronized (mLock) {
proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
- proto.write(VibratorManagerServiceDumpProto.KEYBOARD_VIBRATION_ON,
- mKeyboardVibrationOn);
proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
getCurrentIntensity(USAGE_ALARM));
@@ -774,6 +760,11 @@
return value;
}
+ @VibrationIntensity
+ private int toIntensity(boolean enabled, @VibrationIntensity int defaultValue) {
+ return enabled ? defaultValue : Vibrator.VIBRATION_INTENSITY_OFF;
+ }
+
private boolean loadBooleanSetting(String settingKey, int userHandle) {
return loadSystemSetting(settingKey, 0, userHandle) != 0;
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 76872cf..f2ad5b9 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -57,6 +57,7 @@
import android.os.VibratorInfo;
import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
import android.os.vibrator.VibratorInfoFactory;
import android.os.vibrator.persistence.ParsedVibration;
@@ -251,8 +252,9 @@
mHandler = injector.createHandler(Looper.myLooper());
mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
- mVibrationSettings = new VibrationSettings(mContext, mHandler);
- mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
+ VibrationConfig vibrationConfig = new VibrationConfig(context.getResources());
+ mVibrationSettings = new VibrationSettings(mContext, mHandler, vibrationConfig);
+ mVibrationScaler = new VibrationScaler(vibrationConfig, mVibrationSettings);
mVibratorControlService = new VibratorControlService(mContext,
injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings,
mFrameworkStatsLogger, mLock);
@@ -1698,7 +1700,7 @@
IBinder.DeathRecipient {
public final ExternalVibration externalVibration;
- public ExternalVibrationScale scale = new ExternalVibrationScale();
+ public final ExternalVibrationScale scale = new ExternalVibrationScale();
private Vibration.Status mStatus;
@@ -1712,8 +1714,18 @@
mStatus = Vibration.Status.RUNNING;
}
+ public void muteScale() {
+ scale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
+ if (Flags.hapticsScaleV2Enabled()) {
+ scale.scaleFactor = 0;
+ }
+ }
+
public void scale(VibrationScaler scaler, int usage) {
scale.scaleLevel = scaler.getScaleLevel(usage);
+ if (Flags.hapticsScaleV2Enabled()) {
+ scale.scaleFactor = scaler.getScaleFactor(usage);
+ }
scale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage);
stats.reportAdaptiveScale(scale.adaptiveHapticsScale);
}
@@ -2047,7 +2059,7 @@
// Create Vibration.Stats as close to the received request as possible, for tracking.
ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
// Mute the request until we run all the checks and accept the vibration.
- vibHolder.scale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
+ vibHolder.muteScale();
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
@@ -2146,7 +2158,7 @@
new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
/* continueExternalControl= */ false);
// Mute the request, vibration will be ignored.
- vibHolder.scale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
+ vibHolder.muteScale();
}
return vibHolder.scale;
}
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index d38edfc..c898747 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -40,6 +40,8 @@
private final AppCompatOverrides mAppCompatOverrides;
@NonNull
private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
+ @NonNull
+ private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
AppCompatController(@NonNull WindowManagerService wmService,
@NonNull ActivityRecord activityRecord) {
@@ -57,6 +59,8 @@
mTransparentPolicy, mAppCompatOverrides);
mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(mActivityRecord,
wmService.mAppCompatConfiguration);
+ mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord,
+ mTransparentPolicy);
}
@NonNull
@@ -113,6 +117,11 @@
}
@NonNull
+ AppCompatLetterboxPolicy getAppCompatLetterboxPolicy() {
+ return mAppCompatLetterboxPolicy;
+ }
+
+ @NonNull
AppCompatFocusOverrides getAppCompatFocusOverrides() {
return mAppCompatOverrides.getAppCompatFocusOverrides();
}
@@ -127,4 +136,9 @@
return mAppCompatDeviceStateQuery;
}
+ @NonNull
+ AppCompatLetterboxOverrides getAppCompatLetterboxOverrides() {
+ return mAppCompatOverrides.getAppCompatLetterboxOverrides();
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java b/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java
new file mode 100644
index 0000000..24ed14c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2024 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.wm;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.graphics.Color;
+import android.util.Slog;
+import android.view.WindowManager;
+
+import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+
+/**
+ * Encapsulates overrides and configuration related to the Letterboxing policy.
+ */
+class AppCompatLetterboxOverrides {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatLetterboxOverrides" : TAG_ATM;
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ private final AppCompatConfiguration mAppCompatConfiguration;
+
+ private boolean mShowWallpaperForLetterboxBackground;
+
+ AppCompatLetterboxOverrides(@NonNull ActivityRecord activityRecord,
+ @NonNull AppCompatConfiguration appCompatConfiguration) {
+ mActivityRecord = activityRecord;
+ mAppCompatConfiguration = appCompatConfiguration;
+ }
+
+ boolean shouldLetterboxHaveRoundedCorners() {
+ // TODO(b/214030873): remove once background is drawn for transparent activities
+ // Letterbox shouldn't have rounded corners if the activity is transparent
+ return mAppCompatConfiguration.isLetterboxActivityCornersRounded()
+ && mActivityRecord.fillsParent();
+ }
+
+ boolean isLetterboxEducationEnabled() {
+ return mAppCompatConfiguration.getIsEducationEnabled();
+ }
+
+ boolean hasWallpaperBackgroundForLetterbox() {
+ return mShowWallpaperForLetterboxBackground;
+ }
+
+ boolean checkWallpaperBackgroundForLetterbox(boolean wallpaperShouldBeShown) {
+ if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
+ mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
+ return true;
+ }
+ return false;
+ }
+
+ @NonNull
+ Color getLetterboxBackgroundColor() {
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (w == null || w.isLetterboxedForDisplayCutout()) {
+ return Color.valueOf(Color.BLACK);
+ }
+ final @LetterboxBackgroundType int letterboxBackgroundType =
+ mAppCompatConfiguration.getLetterboxBackgroundType();
+ final ActivityManager.TaskDescription taskDescription = mActivityRecord.taskDescription;
+ switch (letterboxBackgroundType) {
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColorFloating());
+ }
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColor());
+ }
+ break;
+ case LETTERBOX_BACKGROUND_WALLPAPER:
+ if (hasWallpaperBackgroundForLetterbox()) {
+ // Color is used for translucent scrim that dims wallpaper.
+ return mAppCompatConfiguration.getLetterboxBackgroundColor();
+ }
+ Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
+ + "blur is not supported by a device or not supported in the current "
+ + "window configuration or both alpha scrim and blur radius aren't "
+ + "provided so using solid color background");
+ break;
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ return mAppCompatConfiguration.getLetterboxBackgroundColor();
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox background type: " + letterboxBackgroundType);
+ }
+ // If picked option configured incorrectly or not supported then default to a solid color
+ // background.
+ return mAppCompatConfiguration.getLetterboxBackgroundColor();
+ }
+
+ int getLetterboxActivityCornersRadius() {
+ return mAppCompatConfiguration.getLetterboxActivityCornersRadius();
+ }
+
+ boolean isLetterboxActivityCornersRounded() {
+ return mAppCompatConfiguration.isLetterboxActivityCornersRounded();
+ }
+
+ @LetterboxBackgroundType
+ int getLetterboxBackgroundType() {
+ return mAppCompatConfiguration.getLetterboxBackgroundType();
+ }
+
+ int getLetterboxWallpaperBlurRadiusPx() {
+ int blurRadius = mAppCompatConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx();
+ return Math.max(blurRadius, 0);
+ }
+
+ float getLetterboxWallpaperDarkScrimAlpha() {
+ float alpha = mAppCompatConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
+ // No scrim by default.
+ return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
+ }
+
+ boolean isLetterboxWallpaperBlurSupported() {
+ return mAppCompatConfiguration.mContext.getSystemService(WindowManager.class)
+ .isCrossWindowBlurEnabled();
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
new file mode 100644
index 0000000..7c42847
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2024 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.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.LetterboxDetails;
+import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+
+/**
+ * Encapsulates the logic for the Letterboxing policy.
+ */
+class AppCompatLetterboxPolicy {
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ private final TransparentPolicy mTransparentPolicy;
+
+ @Nullable
+ private Letterbox mLetterbox;
+
+ private final Point mTmpPoint = new Point();
+
+ private boolean mLastShouldShowLetterboxUi;
+
+ AppCompatLetterboxPolicy(@NonNull ActivityRecord activityRecord,
+ @NonNull TransparentPolicy transparentPolicy) {
+ mActivityRecord = activityRecord;
+ mTransparentPolicy = transparentPolicy;
+ }
+
+ /** Cleans up {@link Letterbox} if it exists.*/
+ void destroy() {
+ if (mLetterbox != null) {
+ mLetterbox.destroy();
+ mLetterbox = null;
+ }
+ // TODO Remove after Letterbox refactoring.
+ mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+ .setLetterboxInnerBoundsSupplier(null);
+ }
+
+ void onMovedToDisplay(int displayId) {
+ if (mLetterbox != null) {
+ mLetterbox.onMovedToDisplay(displayId);
+ }
+ }
+
+ /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
+ @NonNull
+ Rect getLetterboxInsets() {
+ if (mLetterbox != null) {
+ return mLetterbox.getInsets();
+ } else {
+ return new Rect();
+ }
+ }
+
+ /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
+ void getLetterboxInnerBounds(@NonNull Rect outBounds) {
+ if (mLetterbox != null) {
+ outBounds.set(mLetterbox.getInnerFrame());
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (w != null) {
+ adjustBoundsForTaskbar(w, outBounds);
+ }
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+
+ /**
+ * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
+ * when the current activity is displayed.
+ */
+ boolean isFullyTransparentBarAllowed(@NonNull Rect rect) {
+ return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
+ }
+
+ void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint,
+ @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction inputT) {
+ if (shouldNotLayoutLetterbox(winHint)) {
+ return;
+ }
+ layoutLetterboxIfNeeded(winHint);
+ if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
+ mLetterbox.applySurfaceChanges(t, inputT);
+ }
+ }
+
+ void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint) {
+ updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction(),
+ mActivityRecord.getPendingTransaction());
+ }
+
+
+ void layoutLetterboxIfNeeded(@NonNull WindowState w) {
+ if (shouldNotLayoutLetterbox(w)) {
+ return;
+ }
+ updateRoundedCornersIfNeeded(w);
+ updateWallpaperForLetterbox(w);
+ if (shouldShowLetterboxUi(w)) {
+ if (mLetterbox == null) {
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ final AppCompatReachabilityPolicy reachabilityPolicy = mActivityRecord
+ .mAppCompatController.getAppCompatReachabilityPolicy();
+ mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
+ mActivityRecord.mWmService.mTransactionFactory,
+ reachabilityPolicy, letterboxOverrides,
+ this::getLetterboxParentSurface);
+ mLetterbox.attachInput(w);
+ mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+ .setLetterboxInnerBoundsSupplier(mLetterbox::getInnerFrame);
+ }
+
+ if (mActivityRecord.isInLetterboxAnimation()) {
+ // In this case we attach the letterbox to the task instead of the activity.
+ mActivityRecord.getTask().getPosition(mTmpPoint);
+ } else {
+ mActivityRecord.getPosition(mTmpPoint);
+ }
+
+ // Get the bounds of the "space-to-fill". The transformed bounds have the highest
+ // priority because the activity is launched in a rotated environment. In multi-window
+ // mode, the taskFragment-level represents this for both split-screen
+ // and activity-embedding. In fullscreen-mode, the task container does
+ // (since the orientation letterbox is also applied to the task).
+ final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
+ final Rect spaceToFill = transformedBounds != null
+ ? transformedBounds
+ : mActivityRecord.inMultiWindowMode()
+ ? mActivityRecord.getTaskFragment().getBounds()
+ : mActivityRecord.getRootTask().getParent().getBounds();
+ // In case of translucent activities an option is to use the WindowState#getFrame() of
+ // the first opaque activity beneath. In some cases (e.g. an opaque activity is using
+ // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct
+ // information and in particular it might provide a value for a smaller area making
+ // the letterbox overlap with the translucent activity's frame.
+ // If we use WindowState#getFrame() for the translucent activity's letterbox inner
+ // frame, the letterbox will then be overlapped with the translucent activity's frame.
+ // Because the surface layer of letterbox is lower than an activity window, this
+ // won't crop the content, but it may affect other features that rely on values stored
+ // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher
+ // For this reason we use ActivityRecord#getBounds() that the translucent activity
+ // inherits from the first opaque activity beneath and also takes care of the scaling
+ // in case of activities in size compat mode.
+ final Rect innerFrame =
+ mTransparentPolicy.isRunning() ? mActivityRecord.getBounds() : w.getFrame();
+ mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
+ if (mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides()
+ .isDoubleTapEvent()) {
+ // We need to notify Shell that letterbox position has changed.
+ mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+ }
+ } else if (mLetterbox != null) {
+ mLetterbox.hide();
+ }
+ }
+
+ @Nullable
+ LetterboxDetails getLetterboxDetails() {
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (mLetterbox == null || w == null || w.isLetterboxedForDisplayCutout()) {
+ return null;
+ }
+ final Rect letterboxInnerBounds = new Rect();
+ final Rect letterboxOuterBounds = new Rect();
+ getLetterboxInnerBounds(letterboxInnerBounds);
+ getLetterboxOuterBounds(letterboxOuterBounds);
+
+ if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) {
+ return null;
+ }
+
+ return new LetterboxDetails(
+ letterboxInnerBounds,
+ letterboxOuterBounds,
+ w.mAttrs.insetsFlags.appearance
+ );
+ }
+
+ @Nullable
+ SurfaceControl getLetterboxParentSurface() {
+ if (mActivityRecord.isInLetterboxAnimation()) {
+ return mActivityRecord.getTask().getSurfaceControl();
+ }
+ return mActivityRecord.getSurfaceControl();
+ }
+
+ @VisibleForTesting
+ boolean shouldShowLetterboxUi(@NonNull WindowState mainWindow) {
+ if (mActivityRecord.mAppCompatController.getAppCompatOrientationOverrides()
+ .getIsRelaunchingAfterRequestedOrientationChanged()) {
+ return mLastShouldShowLetterboxUi;
+ }
+
+ final boolean shouldShowLetterboxUi =
+ (mActivityRecord.isInLetterboxAnimation() || mActivityRecord.isVisible()
+ || mActivityRecord.isVisibleRequested())
+ && mainWindow.areAppWindowBoundsLetterboxed()
+ // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
+ // WindowContainer#showWallpaper because the later will return true when
+ // this activity is using blurred wallpaper for letterbox background.
+ && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
+
+ mLastShouldShowLetterboxUi = shouldShowLetterboxUi;
+
+ return shouldShowLetterboxUi;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ Rect getCropBoundsIfNeeded(@NonNull final WindowState mainWindow) {
+ if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
+ // We don't want corner radius on the window.
+ // In the case the ActivityRecord requires a letterboxed animation we never want
+ // rounded corners on the window because rounded corners are applied at the
+ // animation-bounds surface level and rounded corners on the window would interfere
+ // with that leading to unexpected rounded corner positioning during the animation.
+ return null;
+ }
+
+ final Rect cropBounds = new Rect(mActivityRecord.getBounds());
+
+ // In case of translucent activities we check if the requested size is different from
+ // the size provided using inherited bounds. In that case we decide to not apply rounded
+ // corners because we assume the specific layout would. This is the case when the layout
+ // of the translucent activity uses only a part of all the bounds because of the use of
+ // LayoutParams.WRAP_CONTENT.
+ if (mTransparentPolicy.isRunning() && (cropBounds.width() != mainWindow.mRequestedWidth
+ || cropBounds.height() != mainWindow.mRequestedHeight)) {
+ return null;
+ }
+
+ // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
+ // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
+ // are in screen coordinates
+ adjustBoundsForTaskbar(mainWindow, cropBounds);
+
+ final float scale = mainWindow.mInvGlobalScale;
+ if (scale != 1f && scale > 0f) {
+ cropBounds.scale(scale);
+ }
+
+ // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
+ // control is in the top left corner of an app window so offsetting bounds
+ // accordingly.
+ cropBounds.offsetTo(0, 0);
+ return cropBounds;
+ }
+
+ // Returns rounded corners radius the letterboxed activity should have based on override in
+ // R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+ // Device corners can be different on the right and left sides, but we use the same radius
+ // for all corners for consistency and pick a minimal bottom one for consistency with a
+ // taskbar rounded corners.
+ int getRoundedCornersRadius(@NonNull final WindowState mainWindow) {
+ if (!requiresRoundedCorners(mainWindow)) {
+ return 0;
+ }
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ final int radius;
+ if (letterboxOverrides.getLetterboxActivityCornersRadius() >= 0) {
+ radius = letterboxOverrides.getLetterboxActivityCornersRadius();
+ } else {
+ final InsetsState insetsState = mainWindow.getInsetsState();
+ radius = Math.min(
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
+ }
+
+ final float scale = mainWindow.mInvGlobalScale;
+ return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
+ }
+
+ private int getInsetsStateCornerRadius(@NonNull InsetsState insetsState,
+ @RoundedCorner.Position int position) {
+ RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
+ return corner == null ? 0 : corner.getRadius();
+ }
+
+ private boolean requiresRoundedCorners(@NonNull final WindowState mainWindow) {
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ return isLetterboxedNotForDisplayCutout(mainWindow)
+ && letterboxOverrides.isLetterboxActivityCornersRounded();
+ }
+
+ private void updateRoundedCornersIfNeeded(@NonNull final WindowState mainWindow) {
+ final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
+ if (windowSurface == null || !windowSurface.isValid()) {
+ return;
+ }
+
+ // cropBounds must be non-null for the cornerRadius to be ever applied.
+ mActivityRecord.getSyncTransaction()
+ .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
+ .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
+ }
+
+ private void updateWallpaperForLetterbox(@NonNull WindowState mainWindow) {
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ final @LetterboxBackgroundType int letterboxBackgroundType =
+ letterboxOverrides.getLetterboxBackgroundType();
+ boolean wallpaperShouldBeShown =
+ letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
+ // Don't use wallpaper as a background if letterboxed for display cutout.
+ && isLetterboxedNotForDisplayCutout(mainWindow)
+ // Check that dark scrim alpha or blur radius are provided
+ && (letterboxOverrides.getLetterboxWallpaperBlurRadiusPx() > 0
+ || letterboxOverrides.getLetterboxWallpaperDarkScrimAlpha() > 0)
+ // Check that blur is supported by a device if blur radius is provided.
+ && (letterboxOverrides.getLetterboxWallpaperBlurRadiusPx() <= 0
+ || letterboxOverrides.isLetterboxWallpaperBlurSupported());
+ if (letterboxOverrides.checkWallpaperBackgroundForLetterbox(wallpaperShouldBeShown)) {
+ mActivityRecord.requestUpdateWallpaperIfNeeded();
+ }
+ }
+
+ private boolean isLetterboxedNotForDisplayCutout(@NonNull WindowState mainWindow) {
+ return shouldShowLetterboxUi(mainWindow)
+ && !mainWindow.isLetterboxedForDisplayCutout();
+ }
+
+ private void adjustBoundsForTaskbar(@NonNull final WindowState mainWindow,
+ @NonNull final Rect bounds) {
+ // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
+ // an insets frame is equal to a navigation bar which shouldn't affect position of
+ // rounded corners since apps are expected to handle navigation bar inset.
+ // This condition checks whether the taskbar is visible.
+ // Do not crop the taskbar inset if the window is in immersive mode - the user can
+ // swipe to show/hide the taskbar as an overlay.
+ // Adjust the bounds only in case there is an expanded taskbar,
+ // otherwise the rounded corners will be shown behind the navbar.
+ final InsetsSource expandedTaskbarOrNull =
+ AppCompatUtils.getExpandedTaskbarOrNull(mainWindow);
+ if (expandedTaskbarOrNull != null) {
+ // Rounded corners should be displayed above the expanded taskbar.
+ bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
+ }
+ }
+
+ private static boolean shouldNotLayoutLetterbox(@Nullable WindowState w) {
+ if (w == null) {
+ return true;
+ }
+ final int type = w.mAttrs.type;
+ // Allow letterbox to be displayed early for base application or application starting
+ // windows even if it is not on the top z order to prevent flickering when the
+ // letterboxed window is brought to the top
+ return (type != TYPE_BASE_APPLICATION && type != TYPE_APPLICATION_STARTING)
+ || w.mAnimatingExit;
+ }
+
+ /** Gets the outer bounds of letterbox. The bounds will be empty if there is no letterbox. */
+ private void getLetterboxOuterBounds(@NonNull Rect outBounds) {
+ if (mLetterbox != null) {
+ outBounds.set(mLetterbox.getOuterFrame());
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index 80bbee3..2f03105 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -37,6 +37,8 @@
private final AppCompatResizeOverrides mAppCompatResizeOverrides;
@NonNull
private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
+ @NonNull
+ private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
AppCompatOverrides(@NonNull ActivityRecord activityRecord,
@NonNull AppCompatConfiguration appCompatConfiguration,
@@ -54,6 +56,8 @@
mAppCompatFocusOverrides = new AppCompatFocusOverrides(activityRecord,
appCompatConfiguration, optPropBuilder);
mAppCompatResizeOverrides = new AppCompatResizeOverrides(activityRecord, optPropBuilder);
+ mAppCompatLetterboxOverrides = new AppCompatLetterboxOverrides(activityRecord,
+ appCompatConfiguration);
}
@NonNull
@@ -85,4 +89,9 @@
AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
return mAppCompatReachabilityOverrides;
}
+
+ @NonNull
+ AppCompatLetterboxOverrides getAppCompatLetterboxOverrides() {
+ return mAppCompatLetterboxOverrides;
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 0244d27..91205fc 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -28,6 +28,9 @@
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.WindowInsets;
import java.util.function.BooleanSupplier;
@@ -212,6 +215,23 @@
return "UNKNOWN_REASON";
}
+ /**
+ * Returns the taskbar in case it is visible and expanded in height, otherwise returns null.
+ */
+ @Nullable
+ static InsetsSource getExpandedTaskbarOrNull(@NonNull final WindowState mainWindow) {
+ final InsetsState state = mainWindow.getInsetsState();
+ for (int i = state.sourceSize() - 1; i >= 0; i--) {
+ final InsetsSource source = state.sourceAt(i);
+ if (source.getType() == WindowInsets.Type.navigationBars()
+ && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)
+ && source.isVisible()) {
+ return source;
+ }
+ }
+ return null;
+ }
+
private static void clearAppCompatTaskInfo(@NonNull AppCompatTaskInfo info) {
info.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b4c7557..fe5b142 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -761,7 +761,7 @@
if (isMonitorForRemote()) {
mObserver.sendResult(null /* result */);
}
- if (isMonitorAnimationOrTransition()) {
+ if (isMonitorAnimationOrTransition() && canCancelAnimations()) {
clearBackAnimations(true /* cancel */);
}
cancelPendingAnimation();
@@ -1935,8 +1935,7 @@
for (int i = penActivities.length - 1; i >= 0; --i) {
ActivityRecord resetActivity = penActivities[i];
if (transition.isInTransition(resetActivity)) {
- resetActivity.mTransitionController.setReady(
- resetActivity.getDisplayContent(), true);
+ transition.setReady(resetActivity.getDisplayContent(), true);
return true;
}
}
@@ -1991,18 +1990,12 @@
activity.makeVisibleIfNeeded(null /* starting */, true /* notifyToClient */);
}
}
- boolean needTransition = false;
- final DisplayContent dc = affects.get(0).getDisplayContent();
- for (int i = affects.size() - 1; i >= 0; --i) {
- final ActivityRecord activity = affects.get(i);
- needTransition |= tc.isCollecting(activity);
- }
if (prepareOpen != null) {
- if (needTransition) {
+ if (prepareOpen.hasChanges()) {
tc.requestStartTransition(prepareOpen,
null /*startTask */, null /* remoteTransition */,
null /* displayChange */);
- tc.setReady(dc);
+ prepareOpen.setReady(affects.get(0), true);
return prepareOpen;
} else {
prepareOpen.abort();
@@ -2053,11 +2046,22 @@
}
}
+ /** If the open transition is playing, wait for transition to clear the animation */
+ private boolean canCancelAnimations() {
+ if (!Flags.migratePredictiveBackTransition()) {
+ return true;
+ }
+ return mAnimationHandler.mOpenAnimAdaptor == null
+ || mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition == null;
+ }
+
void startAnimation() {
if (!mBackAnimationInProgress) {
// gesture is already finished, do not start animation
if (mPendingAnimation != null) {
- clearBackAnimations(true /* cancel */);
+ if (canCancelAnimations()) {
+ clearBackAnimations(true /* cancel */);
+ }
mPendingAnimation = null;
}
return;
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 7a95c2d..2f0ee17 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -129,21 +129,33 @@
@WindowConfiguration.WindowingMode
private int getWindowingModeLocked(@NonNull SettingsProvider.SettingsEntry settings,
@NonNull DisplayContent dc) {
- int windowingMode = settings.mWindowingMode;
+ final int windowingModeFromDisplaySettings = settings.mWindowingMode;
// This display used to be in freeform, but we don't support freeform anymore, so fall
// back to fullscreen.
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
+ if (windowingModeFromDisplaySettings == WindowConfiguration.WINDOWING_MODE_FREEFORM
&& !mService.mAtmService.mSupportsFreeformWindowManagement) {
return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
}
- // No record is present so use default windowing mode policy.
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement
- && (mService.mIsPc || dc.forceDesktopMode())
- ? WindowConfiguration.WINDOWING_MODE_FREEFORM
- : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ if (windowingModeFromDisplaySettings != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ return windowingModeFromDisplaySettings;
}
- return windowingMode;
+ // No record is present so use default windowing mode policy.
+ final boolean forceFreeForm = mService.mAtmService.mSupportsFreeformWindowManagement
+ && (mService.mIsPc || dc.forceDesktopMode());
+ if (forceFreeForm) {
+ return WindowConfiguration.WINDOWING_MODE_FREEFORM;
+ }
+ final int currentWindowingMode = dc.getDefaultTaskDisplayArea().getWindowingMode();
+ if (currentWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ // No record preset in settings + no mode set via the display area policy.
+ // Move to fullscreen as a fallback.
+ return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ }
+ if (currentWindowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
+ // Freeform was enabled before but disabled now, the TDA should now move to fullscreen.
+ return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ }
+ return currentWindowingMode;
}
@WindowConfiguration.WindowingMode
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3fc5eaf..252590e 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -38,9 +38,6 @@
import com.android.server.UiThread;
-import java.util.function.BooleanSupplier;
-import java.util.function.DoubleSupplier;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
/**
@@ -54,12 +51,6 @@
private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
- private final BooleanSupplier mAreCornersRounded;
- private final Supplier<Color> mColorSupplier;
- // Parameters for "blurred wallpaper" letterbox background.
- private final BooleanSupplier mHasWallpaperBackgroundSupplier;
- private final IntSupplier mBlurRadiusSupplier;
- private final DoubleSupplier mDarkScrimAlphaSupplier;
private final Supplier<SurfaceControl> mParentSurfaceSupplier;
private final Rect mOuter = new Rect();
@@ -77,6 +68,8 @@
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
@NonNull
private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
+ @NonNull
+ private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
/**
* Constructs a Letterbox.
@@ -85,24 +78,14 @@
*/
public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- BooleanSupplier areCornersRounded,
- Supplier<Color> colorSupplier,
- BooleanSupplier hasWallpaperBackgroundSupplier,
- IntSupplier blurRadiusSupplier,
- DoubleSupplier darkScrimAlphaSupplier,
@NonNull AppCompatReachabilityPolicy appCompatReachabilityPolicy,
+ @NonNull AppCompatLetterboxOverrides appCompatLetterboxOverrides,
Supplier<SurfaceControl> parentSurface) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
- mAreCornersRounded = areCornersRounded;
- mColorSupplier = colorSupplier;
- mHasWallpaperBackgroundSupplier = hasWallpaperBackgroundSupplier;
- mBlurRadiusSupplier = blurRadiusSupplier;
- mDarkScrimAlphaSupplier = darkScrimAlphaSupplier;
mAppCompatReachabilityPolicy = appCompatReachabilityPolicy;
+ mAppCompatLetterboxOverrides = appCompatLetterboxOverrides;
mParentSurfaceSupplier = parentSurface;
- // TODO Remove after Letterbox refactoring.
- mAppCompatReachabilityPolicy.setLetterboxInnerBoundsSupplier(this::getInnerFrame);
}
/**
@@ -252,7 +235,8 @@
* Returns {@code true} when using {@link #mFullWindowSurface} instead of {@link mSurfaces}.
*/
private boolean useFullWindowSurface() {
- return mAreCornersRounded.getAsBoolean() || mHasWallpaperBackgroundSupplier.getAsBoolean();
+ return mAppCompatLetterboxOverrides.shouldLetterboxHaveRoundedCorners()
+ || mAppCompatLetterboxOverrides.hasWallpaperBackgroundForLetterbox();
}
private final class TapEventReceiver extends InputEventReceiver {
@@ -431,7 +415,7 @@
createSurface(t);
}
- mColor = mColorSupplier.get();
+ mColor = mAppCompatLetterboxOverrides.getLetterboxBackgroundColor();
mParentSurface = mParentSurfaceSupplier.get();
t.setColor(mSurface, getRgbColorArray());
t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
@@ -439,7 +423,8 @@
mSurfaceFrameRelative.height());
t.reparent(mSurface, mParentSurface);
- mHasWallpaperBackground = mHasWallpaperBackgroundSupplier.getAsBoolean();
+ mHasWallpaperBackground = mAppCompatLetterboxOverrides
+ .hasWallpaperBackgroundForLetterbox();
updateAlphaAndBlur(t);
t.show(mSurface);
@@ -460,17 +445,19 @@
t.setBackgroundBlurRadius(mSurface, 0);
return;
}
- final float alpha = (float) mDarkScrimAlphaSupplier.getAsDouble();
+ final float alpha = mAppCompatLetterboxOverrides.getLetterboxWallpaperDarkScrimAlpha();
t.setAlpha(mSurface, alpha);
// Translucent dark scrim can be shown without blur.
- if (mBlurRadiusSupplier.getAsInt() <= 0) {
+ final int blurRadiusPx = mAppCompatLetterboxOverrides
+ .getLetterboxWallpaperBlurRadiusPx();
+ if (blurRadiusPx <= 0) {
// Removing pre-exesting blur
t.setBackgroundBlurRadius(mSurface, 0);
return;
}
- t.setBackgroundBlurRadius(mSurface, mBlurRadiusSupplier.getAsInt());
+ t.setBackgroundBlurRadius(mSurface, blurRadiusPx);
}
private float[] getRgbColorArray() {
@@ -487,8 +474,9 @@
// and mParentSurface may never be updated in applySurfaceChanges but this
// doesn't mean that update is needed.
|| !mSurfaceFrameRelative.isEmpty()
- && (mHasWallpaperBackgroundSupplier.getAsBoolean() != mHasWallpaperBackground
- || !mColorSupplier.get().equals(mColor)
+ && (mAppCompatLetterboxOverrides.hasWallpaperBackgroundForLetterbox()
+ != mHasWallpaperBackground
+ || !mAppCompatLetterboxOverrides.getLetterboxBackgroundColor().equals(mColor)
|| mParentSurfaceSupplier.get() != mParentSurface);
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 4740fc4..3e54060 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -16,36 +16,19 @@
package com.android.server.wm;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager.TaskDescription;
import android.graphics.Color;
-import android.graphics.Point;
import android.graphics.Rect;
-import android.util.Slog;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.RoundedCorner;
-import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import android.view.WindowInsets;
-import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.LetterboxDetails;
-import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
import java.io.PrintWriter;
@@ -56,8 +39,6 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
- private final Point mTmpPoint = new Point();
-
private final AppCompatConfiguration mAppCompatConfiguration;
private final ActivityRecord mActivityRecord;
@@ -66,18 +47,9 @@
@NonNull
private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
@NonNull
- private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
+ private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
@NonNull
- private final TransparentPolicy mTransparentPolicy;
- @NonNull
- private final AppCompatOrientationOverrides mAppCompatOrientationOverrides;
-
- private boolean mShowWallpaperForLetterboxBackground;
-
- @Nullable
- private Letterbox mLetterbox;
-
- private boolean mLastShouldShowLetterboxUi;
+ private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
mAppCompatConfiguration = wmService.mAppCompatConfiguration;
@@ -88,63 +60,34 @@
// TODO(b/356385137): Remove these we added to make dependencies temporarily explicit.
mAppCompatReachabilityOverrides = mActivityRecord.mAppCompatController
.getAppCompatReachabilityOverrides();
- mAppCompatReachabilityPolicy = mActivityRecord.mAppCompatController
- .getAppCompatReachabilityPolicy();
- mTransparentPolicy = mActivityRecord.mAppCompatController.getTransparentPolicy();
- mAppCompatOrientationOverrides = mActivityRecord.mAppCompatController
- .getAppCompatOrientationOverrides();
+ mAppCompatLetterboxPolicy = mActivityRecord.mAppCompatController
+ .getAppCompatLetterboxPolicy();
+ mAppCompatLetterboxOverrides = mActivityRecord.mAppCompatController
+ .getAppCompatLetterboxOverrides();
}
/** Cleans up {@link Letterbox} if it exists.*/
void destroy() {
- if (mLetterbox != null) {
- mLetterbox.destroy();
- mLetterbox = null;
- // TODO Remove after Letterbox refactoring.
- mAppCompatReachabilityPolicy.setLetterboxInnerBoundsSupplier(null);
- }
+ mAppCompatLetterboxPolicy.destroy();
}
void onMovedToDisplay(int displayId) {
- if (mLetterbox != null) {
- mLetterbox.onMovedToDisplay(displayId);
- }
+ mAppCompatLetterboxPolicy.onMovedToDisplay(displayId);
}
boolean hasWallpaperBackgroundForLetterbox() {
- return mShowWallpaperForLetterboxBackground;
+ return mAppCompatLetterboxOverrides.hasWallpaperBackgroundForLetterbox();
}
/** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
Rect getLetterboxInsets() {
- if (mLetterbox != null) {
- return mLetterbox.getInsets();
- } else {
- return new Rect();
- }
+ return mAppCompatLetterboxPolicy.getLetterboxInsets();
}
/** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
void getLetterboxInnerBounds(Rect outBounds) {
- if (mLetterbox != null) {
- outBounds.set(mLetterbox.getInnerFrame());
- final WindowState w = mActivityRecord.findMainWindow();
- if (w != null) {
- adjustBoundsForTaskbar(w, outBounds);
- }
- } else {
- outBounds.setEmpty();
- }
- }
-
- /** Gets the outer bounds of letterbox. The bounds will be empty if there is no letterbox. */
- private void getLetterboxOuterBounds(Rect outBounds) {
- if (mLetterbox != null) {
- outBounds.set(mLetterbox.getOuterFrame());
- } else {
- outBounds.setEmpty();
- }
+ mAppCompatLetterboxPolicy.getLetterboxInnerBounds(outBounds);
}
/**
@@ -152,234 +95,39 @@
* when the current activity is displayed.
*/
boolean isFullyTransparentBarAllowed(Rect rect) {
- return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
+ return mAppCompatLetterboxPolicy.isFullyTransparentBarAllowed(rect);
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
- updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction(),
- mActivityRecord.getPendingTransaction());
+ mAppCompatLetterboxPolicy.updateLetterboxSurfaceIfNeeded(winHint);
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint, @NonNull Transaction t,
@NonNull Transaction inputT) {
- if (shouldNotLayoutLetterbox(winHint)) {
- return;
- }
- layoutLetterboxIfNeeded(winHint);
- if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(t, inputT);
- }
+ mAppCompatLetterboxPolicy.updateLetterboxSurfaceIfNeeded(winHint, t, inputT);
}
void layoutLetterboxIfNeeded(WindowState w) {
- if (shouldNotLayoutLetterbox(w)) {
- return;
- }
- updateRoundedCornersIfNeeded(w);
- updateWallpaperForLetterbox(w);
- if (shouldShowLetterboxUi(w)) {
- if (mLetterbox == null) {
- mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
- mActivityRecord.mWmService.mTransactionFactory,
- this::shouldLetterboxHaveRoundedCorners,
- this::getLetterboxBackgroundColor,
- this::hasWallpaperBackgroundForLetterbox,
- this::getLetterboxWallpaperBlurRadiusPx,
- this::getLetterboxWallpaperDarkScrimAlpha,
- mAppCompatReachabilityPolicy,
- this::getLetterboxParentSurface);
- mLetterbox.attachInput(w);
- }
-
- if (mActivityRecord.isInLetterboxAnimation()) {
- // In this case we attach the letterbox to the task instead of the activity.
- mActivityRecord.getTask().getPosition(mTmpPoint);
- } else {
- mActivityRecord.getPosition(mTmpPoint);
- }
-
- // Get the bounds of the "space-to-fill". The transformed bounds have the highest
- // priority because the activity is launched in a rotated environment. In multi-window
- // mode, the taskFragment-level represents this for both split-screen
- // and activity-embedding. In fullscreen-mode, the task container does
- // (since the orientation letterbox is also applied to the task).
- final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
- final Rect spaceToFill = transformedBounds != null
- ? transformedBounds
- : mActivityRecord.inMultiWindowMode()
- ? mActivityRecord.getTaskFragment().getBounds()
- : mActivityRecord.getRootTask().getParent().getBounds();
- // In case of translucent activities an option is to use the WindowState#getFrame() of
- // the first opaque activity beneath. In some cases (e.g. an opaque activity is using
- // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct
- // information and in particular it might provide a value for a smaller area making
- // the letterbox overlap with the translucent activity's frame.
- // If we use WindowState#getFrame() for the translucent activity's letterbox inner
- // frame, the letterbox will then be overlapped with the translucent activity's frame.
- // Because the surface layer of letterbox is lower than an activity window, this
- // won't crop the content, but it may affect other features that rely on values stored
- // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher
- // For this reason we use ActivityRecord#getBounds() that the translucent activity
- // inherits from the first opaque activity beneath and also takes care of the scaling
- // in case of activities in size compat mode.
- final Rect innerFrame =
- mTransparentPolicy.isRunning() ? mActivityRecord.getBounds() : w.getFrame();
- mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
- if (mAppCompatReachabilityOverrides.isDoubleTapEvent()) {
- // We need to notify Shell that letterbox position has changed.
- mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
- }
- } else if (mLetterbox != null) {
- mLetterbox.hide();
- }
- }
-
- SurfaceControl getLetterboxParentSurface() {
- if (mActivityRecord.isInLetterboxAnimation()) {
- return mActivityRecord.getTask().getSurfaceControl();
- }
- return mActivityRecord.getSurfaceControl();
- }
-
- private static boolean shouldNotLayoutLetterbox(WindowState w) {
- if (w == null) {
- return true;
- }
- final int type = w.mAttrs.type;
- // Allow letterbox to be displayed early for base application or application starting
- // windows even if it is not on the top z order to prevent flickering when the
- // letterboxed window is brought to the top
- return (type != TYPE_BASE_APPLICATION && type != TYPE_APPLICATION_STARTING)
- || w.mAnimatingExit;
- }
-
- private boolean shouldLetterboxHaveRoundedCorners() {
- // TODO(b/214030873): remove once background is drawn for transparent activities
- // Letterbox shouldn't have rounded corners if the activity is transparent
- return mAppCompatConfiguration.isLetterboxActivityCornersRounded()
- && mActivityRecord.fillsParent();
+ mAppCompatLetterboxPolicy.layoutLetterboxIfNeeded(w);
}
boolean isLetterboxEducationEnabled() {
- return mAppCompatConfiguration.getIsEducationEnabled();
+ return mAppCompatLetterboxOverrides.isLetterboxEducationEnabled();
}
@VisibleForTesting
boolean shouldShowLetterboxUi(WindowState mainWindow) {
- if (mAppCompatOrientationOverrides.getIsRelaunchingAfterRequestedOrientationChanged()) {
- return mLastShouldShowLetterboxUi;
- }
-
- final boolean shouldShowLetterboxUi =
- (mActivityRecord.isInLetterboxAnimation() || mActivityRecord.isVisible()
- || mActivityRecord.isVisibleRequested())
- && mainWindow.areAppWindowBoundsLetterboxed()
- // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
- // WindowContainer#showWallpaper because the later will return true when this
- // activity is using blurred wallpaper for letterbox background.
- && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
-
- mLastShouldShowLetterboxUi = shouldShowLetterboxUi;
-
- return shouldShowLetterboxUi;
+ return mAppCompatLetterboxPolicy.shouldShowLetterboxUi(mainWindow);
}
Color getLetterboxBackgroundColor() {
- final WindowState w = mActivityRecord.findMainWindow();
- if (w == null || w.isLetterboxedForDisplayCutout()) {
- return Color.valueOf(Color.BLACK);
- }
- @LetterboxBackgroundType int letterboxBackgroundType =
- mAppCompatConfiguration.getLetterboxBackgroundType();
- TaskDescription taskDescription = mActivityRecord.taskDescription;
- switch (letterboxBackgroundType) {
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
- if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
- return Color.valueOf(taskDescription.getBackgroundColorFloating());
- }
- break;
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
- if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
- return Color.valueOf(taskDescription.getBackgroundColor());
- }
- break;
- case LETTERBOX_BACKGROUND_WALLPAPER:
- if (hasWallpaperBackgroundForLetterbox()) {
- // Color is used for translucent scrim that dims wallpaper.
- return mAppCompatConfiguration.getLetterboxBackgroundColor();
- }
- Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
- + "blur is not supported by a device or not supported in the current "
- + "window configuration or both alpha scrim and blur radius aren't "
- + "provided so using solid color background");
- break;
- case LETTERBOX_BACKGROUND_SOLID_COLOR:
- return mAppCompatConfiguration.getLetterboxBackgroundColor();
- default:
- throw new AssertionError(
- "Unexpected letterbox background type: " + letterboxBackgroundType);
- }
- // If picked option configured incorrectly or not supported then default to a solid color
- // background.
- return mAppCompatConfiguration.getLetterboxBackgroundColor();
- }
-
- private void updateRoundedCornersIfNeeded(final WindowState mainWindow) {
- final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
- if (windowSurface == null || !windowSurface.isValid()) {
- return;
- }
-
- // cropBounds must be non-null for the cornerRadius to be ever applied.
- mActivityRecord.getSyncTransaction()
- .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
- .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
+ return mAppCompatLetterboxOverrides.getLetterboxBackgroundColor();
}
@VisibleForTesting
@Nullable
Rect getCropBoundsIfNeeded(final WindowState mainWindow) {
- if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
- // We don't want corner radius on the window.
- // In the case the ActivityRecord requires a letterboxed animation we never want
- // rounded corners on the window because rounded corners are applied at the
- // animation-bounds surface level and rounded corners on the window would interfere
- // with that leading to unexpected rounded corner positioning during the animation.
- return null;
- }
-
- final Rect cropBounds = new Rect(mActivityRecord.getBounds());
-
- // In case of translucent activities we check if the requested size is different from
- // the size provided using inherited bounds. In that case we decide to not apply rounded
- // corners because we assume the specific layout would. This is the case when the layout
- // of the translucent activity uses only a part of all the bounds because of the use of
- // LayoutParams.WRAP_CONTENT.
- if (mTransparentPolicy.isRunning() && (cropBounds.width() != mainWindow.mRequestedWidth
- || cropBounds.height() != mainWindow.mRequestedHeight)) {
- return null;
- }
-
- // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
- // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
- // are in screen coordinates
- adjustBoundsForTaskbar(mainWindow, cropBounds);
-
- final float scale = mainWindow.mInvGlobalScale;
- if (scale != 1f && scale > 0f) {
- cropBounds.scale(scale);
- }
-
- // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
- // control is in the top left corner of an app window so offsetting bounds
- // accordingly.
- cropBounds.offsetTo(0, 0);
- return cropBounds;
- }
-
- private boolean requiresRoundedCorners(final WindowState mainWindow) {
- return isLetterboxedNotForDisplayCutout(mainWindow)
- && mAppCompatConfiguration.isLetterboxActivityCornersRounded();
+ return mAppCompatLetterboxPolicy.getCropBoundsIfNeeded(mainWindow);
}
// Returns rounded corners radius the letterboxed activity should have based on override in
@@ -388,102 +136,12 @@
// for all corners for consistency and pick a minimal bottom one for consistency with a
// taskbar rounded corners.
int getRoundedCornersRadius(final WindowState mainWindow) {
- if (!requiresRoundedCorners(mainWindow)) {
- return 0;
- }
-
- final int radius;
- if (mAppCompatConfiguration.getLetterboxActivityCornersRadius() >= 0) {
- radius = mAppCompatConfiguration.getLetterboxActivityCornersRadius();
- } else {
- final InsetsState insetsState = mainWindow.getInsetsState();
- radius = Math.min(
- getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
- getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
- }
-
- final float scale = mainWindow.mInvGlobalScale;
- return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
+ return mAppCompatLetterboxPolicy.getRoundedCornersRadius(mainWindow);
}
- /**
- * Returns the taskbar in case it is visible and expanded in height, otherwise returns null.
- */
- @VisibleForTesting
@Nullable
- InsetsSource getExpandedTaskbarOrNull(final WindowState mainWindow) {
- final InsetsState state = mainWindow.getInsetsState();
- for (int i = state.sourceSize() - 1; i >= 0; i--) {
- final InsetsSource source = state.sourceAt(i);
- if (source.getType() == WindowInsets.Type.navigationBars()
- && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)
- && source.isVisible()) {
- return source;
- }
- }
- return null;
- }
-
- private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) {
- // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
- // an insets frame is equal to a navigation bar which shouldn't affect position of
- // rounded corners since apps are expected to handle navigation bar inset.
- // This condition checks whether the taskbar is visible.
- // Do not crop the taskbar inset if the window is in immersive mode - the user can
- // swipe to show/hide the taskbar as an overlay.
- // Adjust the bounds only in case there is an expanded taskbar,
- // otherwise the rounded corners will be shown behind the navbar.
- final InsetsSource expandedTaskbarOrNull = getExpandedTaskbarOrNull(mainWindow);
- if (expandedTaskbarOrNull != null) {
- // Rounded corners should be displayed above the expanded taskbar.
- bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
- }
- }
-
- private int getInsetsStateCornerRadius(
- InsetsState insetsState, @RoundedCorner.Position int position) {
- RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
- return corner == null ? 0 : corner.getRadius();
- }
-
- private boolean isLetterboxedNotForDisplayCutout(WindowState mainWindow) {
- return shouldShowLetterboxUi(mainWindow)
- && !mainWindow.isLetterboxedForDisplayCutout();
- }
-
- private void updateWallpaperForLetterbox(WindowState mainWindow) {
- @LetterboxBackgroundType int letterboxBackgroundType =
- mAppCompatConfiguration.getLetterboxBackgroundType();
- boolean wallpaperShouldBeShown =
- letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
- // Don't use wallpaper as a background if letterboxed for display cutout.
- && isLetterboxedNotForDisplayCutout(mainWindow)
- // Check that dark scrim alpha or blur radius are provided
- && (getLetterboxWallpaperBlurRadiusPx() > 0
- || getLetterboxWallpaperDarkScrimAlpha() > 0)
- // Check that blur is supported by a device if blur radius is provided.
- && (getLetterboxWallpaperBlurRadiusPx() <= 0
- || isLetterboxWallpaperBlurSupported());
- if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
- mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
- mActivityRecord.requestUpdateWallpaperIfNeeded();
- }
- }
-
- private int getLetterboxWallpaperBlurRadiusPx() {
- int blurRadius = mAppCompatConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx();
- return Math.max(blurRadius, 0);
- }
-
- private float getLetterboxWallpaperDarkScrimAlpha() {
- float alpha = mAppCompatConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
- // No scrim by default.
- return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
- }
-
- private boolean isLetterboxWallpaperBlurSupported() {
- return mAppCompatConfiguration.mContext.getSystemService(WindowManager.class)
- .isCrossWindowBlurEnabled();
+ LetterboxDetails getLetterboxDetails() {
+ return mAppCompatLetterboxPolicy.getLetterboxDetails();
}
void dump(PrintWriter pw, String prefix) {
@@ -526,11 +184,11 @@
if (mAppCompatConfiguration.getLetterboxBackgroundType()
== LETTERBOX_BACKGROUND_WALLPAPER) {
pw.println(prefix + " isLetterboxWallpaperBlurSupported="
- + isLetterboxWallpaperBlurSupported());
+ + mAppCompatLetterboxOverrides.isLetterboxWallpaperBlurSupported());
pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha="
- + getLetterboxWallpaperDarkScrimAlpha());
+ + mAppCompatLetterboxOverrides.getLetterboxWallpaperDarkScrimAlpha());
pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius="
- + getLetterboxWallpaperBlurRadiusPx());
+ + mAppCompatLetterboxOverrides.getLetterboxWallpaperBlurRadiusPx());
}
final AppCompatReachabilityOverrides reachabilityOverrides = mActivityRecord
.mAppCompatController.getAppCompatReachabilityOverrides();
@@ -560,26 +218,4 @@
+ mAppCompatConfiguration
.getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
}
-
- @Nullable
- LetterboxDetails getLetterboxDetails() {
- final WindowState w = mActivityRecord.findMainWindow();
- if (mLetterbox == null || w == null || w.isLetterboxedForDisplayCutout()) {
- return null;
- }
- Rect letterboxInnerBounds = new Rect();
- Rect letterboxOuterBounds = new Rect();
- getLetterboxInnerBounds(letterboxInnerBounds);
- getLetterboxOuterBounds(letterboxOuterBounds);
-
- if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) {
- return null;
- }
-
- return new LetterboxDetails(
- letterboxInnerBounds,
- letterboxOuterBounds,
- w.mAttrs.insetsFlags.appearance
- );
- }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d3df5fd..12e91ad 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3507,7 +3507,7 @@
| StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS);
if ((info.startingWindowTypeParameter
& StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) {
- final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION);
+ final WindowState topMainWin = getTopFullscreenMainWindow();
if (topMainWin != null) {
info.mainWindowLayoutParams = topMainWin.getAttrs();
info.requestedVisibleTypes = topMainWin.getRequestedVisibleTypes();
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3139e18..486a61b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -3354,6 +3354,15 @@
return chg.hasChanged();
}
+ boolean hasChanges() {
+ for (int i = 0; i < mParticipants.size(); ++i) {
+ if (mChanges.get(mParticipants.valueAt(i)).hasChanged()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@VisibleForTesting
static class ChangeInfo {
private static final int FLAG_NONE = 0;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 669a999..a08af72 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -2080,10 +2080,14 @@
String tag = parser.getName();
switch (tag) {
case TAG_LOCAL_POLICY_ENTRY:
- readLocalPoliciesInner(parser);
+ int userId = parser.getAttributeInt(/* namespace= */ null, ATTR_USER_ID);
+ if (!mLocalPolicies.contains(userId)) {
+ mLocalPolicies.put(userId, new HashMap<>());
+ }
+ readPoliciesInner(parser, mLocalPolicies.get(userId));
break;
case TAG_GLOBAL_POLICY_ENTRY:
- readGlobalPoliciesInner(parser);
+ readPoliciesInner(parser, mGlobalPolicies);
break;
case TAG_ENFORCING_ADMINS_ENTRY:
readEnforcingAdminsInner(parser);
@@ -2100,64 +2104,45 @@
}
}
- private void readLocalPoliciesInner(TypedXmlPullParser parser)
- throws XmlPullParserException, IOException {
- int userId = parser.getAttributeInt(/* namespace= */ null, ATTR_USER_ID);
- PolicyKey policyKey = null;
- PolicyState<?> policyState = null;
- int outerDepth = parser.getDepth();
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- String tag = parser.getName();
- switch (tag) {
- case TAG_POLICY_KEY_ENTRY:
- policyKey = PolicyDefinition.readPolicyKeyFromXml(parser);
- break;
- case TAG_POLICY_STATE_ENTRY:
- policyState = PolicyState.readFromXml(parser);
- break;
- default:
- Slogf.wtf(TAG, "Unknown tag for local policy entry" + tag);
- }
- }
-
- if (policyKey != null && policyState != null) {
- if (!mLocalPolicies.contains(userId)) {
- mLocalPolicies.put(userId, new HashMap<>());
- }
- mLocalPolicies.get(userId).put(policyKey, policyState);
- } else {
- Slogf.wtf(TAG, "Error parsing local policy, policyKey is "
- + (policyKey == null ? "null" : policyKey) + ", and policyState is "
- + (policyState == null ? "null" : policyState) + ".");
- }
- }
-
- private void readGlobalPoliciesInner(TypedXmlPullParser parser)
+ private static void readPoliciesInner(
+ TypedXmlPullParser parser, Map<PolicyKey, PolicyState<?>> policyStateMap)
throws IOException, XmlPullParserException {
PolicyKey policyKey = null;
+ PolicyDefinition<?> policyDefinition = null;
PolicyState<?> policyState = null;
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
String tag = parser.getName();
switch (tag) {
case TAG_POLICY_KEY_ENTRY:
- policyKey = PolicyDefinition.readPolicyKeyFromXml(parser);
+ if (Flags.dontReadPolicyDefinition()) {
+ policyDefinition = PolicyDefinition.readFromXml(parser);
+ if (policyDefinition != null) {
+ policyKey = policyDefinition.getPolicyKey();
+ }
+ } else {
+ policyKey = PolicyDefinition.readPolicyKeyFromXml(parser);
+ }
break;
case TAG_POLICY_STATE_ENTRY:
- policyState = PolicyState.readFromXml(parser);
+ if (Flags.dontReadPolicyDefinition() && policyDefinition == null) {
+ Slogf.w(TAG, "Skipping policy state - unknown policy definition");
+ } else {
+ policyState = PolicyState.readFromXml(policyDefinition, parser);
+ }
break;
default:
- Slogf.wtf(TAG, "Unknown tag for local policy entry" + tag);
+ Slogf.wtf(TAG, "Unknown tag for policy entry" + tag);
}
}
- if (policyKey != null && policyState != null) {
- mGlobalPolicies.put(policyKey, policyState);
- } else {
- Slogf.wtf(TAG, "Error parsing global policy, policyKey is "
- + (policyKey == null ? "null" : policyKey) + ", and policyState is "
- + (policyState == null ? "null" : policyState) + ".");
+ if (policyKey == null || policyState == null) {
+ Slogf.wtf(TAG, "Error parsing policy, policyKey is %s, and policyState is %s.",
+ policyKey, policyState);
+ return;
}
+
+ policyStateMap.put(policyKey, policyState);
}
private void readEnforcingAdminsInner(TypedXmlPullParser parser)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index f86d307..19a942c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -129,9 +129,8 @@
*/
static PolicyDefinition<Integer> PERMISSION_GRANT(
@NonNull String packageName, @NonNull String permissionName) {
- if (packageName == null || permissionName == null) {
- return GENERIC_PERMISSION_GRANT;
- }
+ Objects.requireNonNull(packageName, "packageName must not be null");
+ Objects.requireNonNull(permissionName, "permissionName must not be null");
return GENERIC_PERMISSION_GRANT.createPolicyDefinition(
new PackagePermissionPolicyKey(
DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY,
@@ -190,10 +189,8 @@
* {@link #GENERIC_PERSISTENT_PREFERRED_ACTIVITY}.
*/
static PolicyDefinition<ComponentName> PERSISTENT_PREFERRED_ACTIVITY(
- IntentFilter intentFilter) {
- if (intentFilter == null) {
- return GENERIC_PERSISTENT_PREFERRED_ACTIVITY;
- }
+ @NonNull IntentFilter intentFilter) {
+ Objects.requireNonNull(intentFilter, "intentFilter must not be null");
return GENERIC_PERSISTENT_PREFERRED_ACTIVITY.createPolicyDefinition(
new IntentFilterPolicyKey(
DevicePolicyIdentifiers.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
@@ -216,11 +213,8 @@
* Passing in {@code null} for {@code packageName} will return
* {@link #GENERIC_PACKAGE_UNINSTALL_BLOCKED}.
*/
- static PolicyDefinition<Boolean> PACKAGE_UNINSTALL_BLOCKED(
- String packageName) {
- if (packageName == null) {
- return GENERIC_PACKAGE_UNINSTALL_BLOCKED;
- }
+ static PolicyDefinition<Boolean> PACKAGE_UNINSTALL_BLOCKED(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
return GENERIC_PACKAGE_UNINSTALL_BLOCKED.createPolicyDefinition(
new PackagePolicyKey(
DevicePolicyIdentifiers.PACKAGE_UNINSTALL_BLOCKED_POLICY, packageName));
@@ -247,10 +241,8 @@
* Passing in {@code null} for {@code packageName} will return
* {@link #GENERIC_APPLICATION_RESTRICTIONS}.
*/
- static PolicyDefinition<Bundle> APPLICATION_RESTRICTIONS(String packageName) {
- if (packageName == null) {
- return GENERIC_APPLICATION_RESTRICTIONS;
- }
+ static PolicyDefinition<Bundle> APPLICATION_RESTRICTIONS(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
return GENERIC_APPLICATION_RESTRICTIONS.createPolicyDefinition(
new PackagePolicyKey(
DevicePolicyIdentifiers.APPLICATION_RESTRICTIONS_POLICY, packageName));
@@ -293,10 +285,8 @@
* Passing in {@code null} for {@code packageName} will return
* {@link #GENERIC_APPLICATION_HIDDEN}.
*/
- static PolicyDefinition<Boolean> APPLICATION_HIDDEN(String packageName) {
- if (packageName == null) {
- return GENERIC_APPLICATION_HIDDEN;
- }
+ static PolicyDefinition<Boolean> APPLICATION_HIDDEN(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
return GENERIC_APPLICATION_HIDDEN.createPolicyDefinition(
new PackagePolicyKey(
DevicePolicyIdentifiers.APPLICATION_HIDDEN_POLICY, packageName));
@@ -319,10 +309,8 @@
* Passing in {@code null} for {@code accountType} will return
* {@link #GENERIC_ACCOUNT_MANAGEMENT_DISABLED}.
*/
- static PolicyDefinition<Boolean> ACCOUNT_MANAGEMENT_DISABLED(String accountType) {
- if (accountType == null) {
- return GENERIC_ACCOUNT_MANAGEMENT_DISABLED;
- }
+ static PolicyDefinition<Boolean> ACCOUNT_MANAGEMENT_DISABLED(@NonNull String accountType) {
+ Objects.requireNonNull(accountType, "accountType must not be null");
return GENERIC_ACCOUNT_MANAGEMENT_DISABLED.createPolicyDefinition(
new AccountTypePolicyKey(
DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY, accountType));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
index 245c438..b813489 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.PolicyValue;
+import android.app.admin.flags.Flags;
import android.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
@@ -254,11 +255,9 @@
}
@Nullable
- static <V> PolicyState<V> readFromXml(TypedXmlPullParser parser)
+ static <V> PolicyState<V> readFromXml(
+ PolicyDefinition<V> policyDefinition, TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
-
- PolicyDefinition<V> policyDefinition = null;
-
PolicyValue<V> currentResolvedPolicy = null;
LinkedHashMap<EnforcingAdmin, PolicyValue<V>> policiesSetByAdmins = new LinkedHashMap<>();
@@ -300,10 +299,15 @@
}
break;
case TAG_POLICY_DEFINITION_ENTRY:
- policyDefinition = PolicyDefinition.readFromXml(parser);
- if (policyDefinition == null) {
- Slogf.wtf(TAG, "Error Parsing TAG_POLICY_DEFINITION_ENTRY, "
- + "PolicyDefinition is null");
+ if (Flags.dontReadPolicyDefinition()) {
+ // Should be passed by the caller.
+ Objects.requireNonNull(policyDefinition);
+ } else {
+ policyDefinition = PolicyDefinition.readFromXml(parser);
+ if (policyDefinition == null) {
+ Slogf.wtf(TAG, "Error Parsing TAG_POLICY_DEFINITION_ENTRY, "
+ + "PolicyDefinition is null");
+ }
}
break;
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index aee7242..a27ad9a 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -913,52 +913,100 @@
final var controller = ControllerImpl.createFrom(null /* currentInstance */, List.of(),
List.of());
- assertNoAction(controller, false /* forHardware */, items);
- assertNoAction(controller, true /* forHardware */, hardwareItems);
+ assertNextItemNoAction(controller, false /* forHardware */, items,
+ null /* expectedNext */);
+ assertNextItemNoAction(controller, true /* forHardware */, hardwareItems,
+ null /* expectedNext */);
}
- /** Verifies that a controller with a single item can't take any actions. */
+ /**
+ * Verifies that a controller with a single item can't update the recency, and cannot switch
+ * away from the item, but allows switching from unknown items to the single item.
+ */
@RequiresFlagsEnabled(Flags.FLAG_IME_SWITCHER_REVAMP)
@Test
public void testSingleItemList() {
final var items = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ null, true /* supportsSwitchingToNextInputMethod */);
+ final var unknownItems = new ArrayList<ImeSubtypeListItem>();
+ addTestImeSubtypeListItems(unknownItems, "UnknownIme", "UnknownIme",
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
final var hardwareItems = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(hardwareItems, "HardwareIme", "HardwareIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ null, true /* supportsSwitchingToNextInputMethod */);
+ final var unknownHardwareItems = new ArrayList<ImeSubtypeListItem>();
+ addTestImeSubtypeListItems(unknownHardwareItems, "HardwareUnknownIme", "HardwareUnknownIme",
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
final var controller = ControllerImpl.createFrom(null /* currentInstance */,
- List.of(items.get(0)), List.of(hardwareItems.get(0)));
+ items, hardwareItems);
- assertNoAction(controller, false /* forHardware */, items);
- assertNoAction(controller, true /* forHardware */, hardwareItems);
+ assertNextItemNoAction(controller, false /* forHardware */, items,
+ null /* expectedNext */);
+ assertNextItemNoAction(controller, false /* forHardware */, unknownItems,
+ items.get(0));
+ assertNextItemNoAction(controller, true /* forHardware */, hardwareItems,
+ null /* expectedNext */);
+ assertNextItemNoAction(controller, true /* forHardware */, unknownHardwareItems,
+ hardwareItems.get(0));
}
- /** Verifies that a controller can't take any actions for unknown items. */
+ /**
+ * Verifies that the recency cannot be updated for unknown items, but switching from unknown
+ * items reaches the most recent known item.
+ */
@RequiresFlagsEnabled(Flags.FLAG_IME_SWITCHER_REVAMP)
@Test
public void testUnknownItems() {
final var items = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
+
+ final var english = items.get(0);
+ final var french = items.get(1);
+ final var italian = items.get(2);
+
final var unknownItems = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(unknownItems, "UnknownIme", "UnknownIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
final var hardwareItems = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(hardwareItems, "HardwareIme", "HardwareIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
final var unknownHardwareItems = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(unknownHardwareItems, "HardwareUnknownIme", "HardwareUnknownIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
hardwareItems);
- assertNoAction(controller, false /* forHardware */, unknownItems);
- assertNoAction(controller, true /* forHardware */, unknownHardwareItems);
+ assertTrue("Recency updated for french IME", onUserAction(controller, french));
+
+ final var recencyItems = List.of(french, english, italian);
+
+ assertNextItemNoAction(controller, false /* forHardware */, unknownItems,
+ french);
+ assertNextItemNoAction(controller, true /* forHardware */, unknownHardwareItems,
+ hardwareItems.get(0));
+
+ // Known items must not be able to switch to unknown items.
+ assertNextOrder(controller, false /* forHardware */, MODE_STATIC, items,
+ List.of(items));
+ assertNextOrder(controller, false /* forHardware */, MODE_RECENT, recencyItems,
+ List.of(recencyItems));
+ assertNextOrder(controller, false /* forHardware */, MODE_AUTO, true /* forward */,
+ recencyItems, List.of(recencyItems));
+ assertNextOrder(controller, false /* forHardware */, MODE_AUTO, false /* forward */,
+ items.reversed(), List.of(items.reversed()));
+
+ assertNextOrder(controller, true /* forHardware */, MODE_STATIC, hardwareItems,
+ List.of(hardwareItems));
+ assertNextOrder(controller, true /* forHardware */, MODE_RECENT, hardwareItems,
+ List.of(hardwareItems));
+ assertNextOrder(controller, true /* forHardware */, MODE_AUTO, hardwareItems,
+ List.of(hardwareItems));
}
/** Verifies that the IME name does influence the comparison order. */
@@ -1199,25 +1247,26 @@
}
/**
- * Verifies that no next items can be found, and the recency cannot be updated for the
+ * Verifies that the expected next item is returned, and the recency cannot be updated for the
* given items.
*
- * @param controller the controller to verify the items on.
- * @param forHardware whether to try finding the next hardware item, or software item.
- * @param items the list of items to verify.
+ * @param controller the controller to verify the items on.
+ * @param forHardware whether to try finding the next hardware item, or software item.
+ * @param items the list of items to verify.
+ * @param expectedNext the expected next item.
*/
- private void assertNoAction(@NonNull ControllerImpl controller, boolean forHardware,
- @NonNull List<ImeSubtypeListItem> items) {
+ private void assertNextItemNoAction(@NonNull ControllerImpl controller, boolean forHardware,
+ @NonNull List<ImeSubtypeListItem> items, @Nullable ImeSubtypeListItem expectedNext) {
for (var item : items) {
for (int mode = MODE_STATIC; mode <= MODE_AUTO; mode++) {
assertNextItem(controller, forHardware, false /* onlyCurrentIme */, mode,
- false /* forward */, item, null /* expectedNext */);
+ false /* forward */, item, expectedNext);
assertNextItem(controller, forHardware, false /* onlyCurrentIme */, mode,
- true /* forward */, item, null /* expectedNext */);
+ true /* forward */, item, expectedNext);
assertNextItem(controller, forHardware, true /* onlyCurrentIme */, mode,
- false /* forward */, item, null /* expectedNext */);
+ false /* forward */, item, expectedNext);
assertNextItem(controller, forHardware, true /* onlyCurrentIme */, mode,
- true /* forward */, item, null /* expectedNext */);
+ true /* forward */, item, expectedNext);
}
assertFalse("User action shouldn't have updated the recency.",
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index b980ca0..30de0e8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -512,6 +512,9 @@
when(mPermissionManagerInternal.getAppOpPermissionPackages(
SCHEDULE_EXACT_ALARM)).thenReturn(EmptyArray.STRING);
+ // Initialize timestamps with arbitrary values of time
+ mNowElapsedTest = 12;
+ mNowRtcTest = 345;
mInjector = new Injector(mMockContext);
mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
@@ -774,6 +777,61 @@
}
@Test
+ public void timeChangeBroadcastForward() throws Exception {
+ final long timeDelta = 12345;
+ // AlarmManagerService sends the broadcast if real time clock proceeds 1000ms more than boot
+ // time clock.
+ mNowRtcTest += timeDelta + 1001;
+ mNowElapsedTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+
+ verify(mMockContext)
+ .sendBroadcastAsUser(
+ argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+ eq(UserHandle.ALL),
+ isNull(),
+ any());
+ }
+
+ @Test
+ public void timeChangeBroadcastBackward() throws Exception {
+ final long timeDelta = 12345;
+ // AlarmManagerService sends the broadcast if real time clock proceeds 1000ms less than boot
+ // time clock.
+ mNowRtcTest += timeDelta - 1001;
+ mNowElapsedTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+
+ verify(mMockContext)
+ .sendBroadcastAsUser(
+ argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+ eq(UserHandle.ALL),
+ isNull(),
+ any());
+ }
+
+ @Test
+ public void timeChangeFilterMinorAdjustment() throws Exception {
+ final long timeDelta = 12345;
+ // AlarmManagerService does not send the broadcast if real time clock proceeds within 1000ms
+ // than boot time clock.
+ mNowRtcTest += timeDelta + 1000;
+ mNowElapsedTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+
+ mNowRtcTest += timeDelta - 1000;
+ mNowElapsedTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+
+ verify(mMockContext, never())
+ .sendBroadcastAsUser(
+ argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+ any(),
+ any(),
+ any());
+ }
+
+ @Test
public void testSingleAlarmExpiration() throws Exception {
final long triggerTime = mNowElapsedTest + 5000;
final PendingIntent alarmPi = getNewMockPendingIntent();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
index 0def516..8753b25 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
@@ -45,7 +45,6 @@
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
-import java.util.concurrent.TimeUnit
import java.util.LinkedList
import java.util.Queue
import android.util.ArraySet
@@ -117,9 +116,6 @@
Mockito.`when`(mockAms.traceManager).thenReturn(mockTraceManager)
mouseKeysInterceptor = MouseKeysInterceptor(mockAms, testLooper.looper, DISPLAY_ID)
- // VirtualMouse is created on a separate thread.
- // Wait for VirtualMouse to be created before running tests
- TimeUnit.MILLISECONDS.sleep(20L)
mouseKeysInterceptor.next = nextInterceptor
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 3d68849..dddab65 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -108,6 +108,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -165,6 +166,7 @@
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SimpleClock;
import android.os.SystemClock;
@@ -197,6 +199,7 @@
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
import com.android.internal.util.test.FsUtil;
@@ -2310,6 +2313,70 @@
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
}
+ @SuppressWarnings("GuardedBy") // For not holding mUidRulesFirstLock
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS)
+ public void testRulesNeverAppliedToCoreUids() throws Exception {
+ clearInvocations(mNetworkManager);
+
+ final int coreAppId = Process.FIRST_APPLICATION_UID - 102;
+ final int coreUid = UserHandle.getUid(USER_ID, coreAppId);
+
+ // Enable all restrictions and add this core uid to all allowlists.
+ mService.mDeviceIdleMode = true;
+ mService.mRestrictPower = true;
+ setRestrictBackground(true);
+ expectHasUseRestrictedNetworksPermission(coreUid, true);
+ enableRestrictedMode(true);
+ final NetworkPolicyManagerInternal internal = LocalServices.getService(
+ NetworkPolicyManagerInternal.class);
+ internal.setLowPowerStandbyActive(true);
+ internal.setLowPowerStandbyAllowlist(new int[]{coreUid});
+ internal.onTempPowerSaveWhitelistChange(coreAppId, true, REASON_OTHER, "testing");
+
+ when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean()))
+ .thenReturn(new int[]{coreAppId});
+ mPowerAllowlistReceiver.onReceive(mServiceContext, null);
+
+ // A normal uid would undergo a rule change from denied to allowed on all chains, but we
+ // should not request any rule change for this core uid.
+ verify(mNetworkManager, never()).setFirewallUidRule(anyInt(), eq(coreUid), anyInt());
+ verify(mNetworkManager, never()).setFirewallUidRules(anyInt(),
+ argThat(ar -> ArrayUtils.contains(ar, coreUid)), any(int[].class));
+ }
+
+ @SuppressWarnings("GuardedBy") // For not holding mUidRulesFirstLock
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS)
+ public void testRulesNeverAppliedToUidsWithoutInternetPermission() throws Exception {
+ clearInvocations(mNetworkManager);
+
+ mService.mInternetPermissionMap.clear();
+ expectHasInternetPermission(UID_A, false);
+
+ // Enable all restrictions and add this uid to all allowlists.
+ mService.mDeviceIdleMode = true;
+ mService.mRestrictPower = true;
+ setRestrictBackground(true);
+ expectHasUseRestrictedNetworksPermission(UID_A, true);
+ enableRestrictedMode(true);
+ final NetworkPolicyManagerInternal internal = LocalServices.getService(
+ NetworkPolicyManagerInternal.class);
+ internal.setLowPowerStandbyActive(true);
+ internal.setLowPowerStandbyAllowlist(new int[]{UID_A});
+ internal.onTempPowerSaveWhitelistChange(APP_ID_A, true, REASON_OTHER, "testing");
+
+ when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean()))
+ .thenReturn(new int[]{APP_ID_A});
+ mPowerAllowlistReceiver.onReceive(mServiceContext, null);
+
+ // A normal uid would undergo a rule change from denied to allowed on all chains, but we
+ // should not request any rule this uid without the INTERNET permission.
+ verify(mNetworkManager, never()).setFirewallUidRule(anyInt(), eq(UID_A), anyInt());
+ verify(mNetworkManager, never()).setFirewallUidRules(anyInt(),
+ argThat(ar -> ArrayUtils.contains(ar, UID_A)), any(int[].class));
+ }
+
private boolean isUidState(int uid, int procState, int procStateSeq, int capability) {
final NetworkPolicyManager.UidState uidState = mService.getUidStateForTest(uid);
if (uidState == null) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index d714db99..7912156 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -121,6 +121,9 @@
// Making a copy of mUsersToRemove to avoid ConcurrentModificationException
mUsersToRemove.stream().toList().forEach(this::removeUser);
mUserRemovalWaiter.close();
+
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ mContext.getUser());
}
private void removeExistingUsers() {
@@ -935,6 +938,35 @@
@MediumTest
@Test
+ @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY)
+ public void testSetUserAdminThrowsSecurityException() throws Exception {
+ UserInfo targetUser = createUser("SecondaryUser", /*flags=*/ 0);
+ assertThat(targetUser.isAdmin()).isFalse();
+
+ try {
+ // 1. Target User Restriction
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true,
+ targetUser.getUserHandle());
+ assertThrows(SecurityException.class, () -> mUserManager.setUserAdmin(targetUser.id));
+
+ // 2. Current User Restriction
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ targetUser.getUserHandle());
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true,
+ mContext.getUser());
+ assertThrows(SecurityException.class, () -> mUserManager.setUserAdmin(targetUser.id));
+
+ } finally {
+ // Ensure restriction is removed even if test fails
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ targetUser.getUserHandle());
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ mContext.getUser());
+ }
+ }
+
+ @MediumTest
+ @Test
public void testRevokeUserAdmin() throws Exception {
UserInfo userInfo = createUser("Admin", /*flags=*/ UserInfo.FLAG_ADMIN);
assertThat(userInfo.isAdmin()).isTrue();
@@ -959,6 +991,37 @@
@MediumTest
@Test
+ @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY)
+ public void testRevokeUserAdminThrowsSecurityException() throws Exception {
+ UserInfo targetUser = createUser("SecondaryUser", /*flags=*/ 0);
+ assertThat(targetUser.isAdmin()).isFalse();
+
+ try {
+ // 1. Target User Restriction
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true,
+ targetUser.getUserHandle());
+ assertThrows(SecurityException.class, () -> mUserManager
+ .revokeUserAdmin(targetUser.id));
+
+ // 2. Current User Restriction
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ targetUser.getUserHandle());
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true,
+ mContext.getUser());
+ assertThrows(SecurityException.class, () -> mUserManager
+ .revokeUserAdmin(targetUser.id));
+
+ } finally {
+ // Ensure restriction is removed even if test fails
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ targetUser.getUserHandle());
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ mContext.getUser());
+ }
+ }
+
+ @MediumTest
+ @Test
public void testGetProfileParent() throws Exception {
assumeManagedUsersSupported();
int mainUserId = mUserManager.getMainUser().getIdentifier();
@@ -1184,6 +1247,23 @@
}
}
+ // Make sure createUser for ADMIN would fail if we have DISALLOW_GRANT_ADMIN.
+ @MediumTest
+ @Test
+ @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY)
+ public void testCreateAdminUser_disallowGrantAdmin() throws Exception {
+ final int creatorId = ActivityManager.getCurrentUser();
+ final UserHandle creatorHandle = asHandle(creatorId);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, creatorHandle);
+ try {
+ UserInfo createdInfo = createUser("SecondaryUser", /*flags=*/ UserInfo.FLAG_ADMIN);
+ assertThat(createdInfo).isNull();
+ } finally {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
+ creatorHandle);
+ }
+ }
+
// Make sure createProfile would fail if we have DISALLOW_ADD_CLONE_PROFILE.
@MediumTest
@Test
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index fad10f7..5518082 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -182,7 +182,7 @@
+ " <device-state>\n"
+ " <identifier>1</identifier>\n"
+ " <properties>\n"
- + " <property>PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS</property>\n"
+ + " <property>com.android.server.policy.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS</property>\n"
+ " </properties>\n"
+ " <conditions/>\n"
+ " </device-state>\n"
@@ -338,11 +338,9 @@
+ " <identifier>4</identifier>\n"
+ " <name>THERMAL_TEST</name>\n"
+ " <properties>\n"
- + " <property>PROPERTY_EMULATED_ONLY</property>\n"
- + " <property>PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL"
- + "</property>\n"
- + " <property>PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE"
- + "</property>\n"
+ + " <property>com.android.server.policy.PROPERTY_EMULATED_ONLY</property>\n"
+ + " <property>com.android.server.policy.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL</property>\n"
+ + " <property>com.android.server.policy.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE</property>\n"
+ " </properties>\n"
+ " </device-state>\n"
+ "</device-state-config>\n";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
index f6e1162..af7f703 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -16,7 +16,6 @@
package com.android.server.notification;
-import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
@@ -31,13 +30,11 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import android.app.Flags;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.content.pm.IPackageManager;
import android.net.Uri;
import android.os.IInterface;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.Condition;
@@ -150,57 +147,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_UI)
- public void notifyConditions_appCannotUndoUserEnablement() {
- ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
- mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
- mock(ServiceConnection.class), 33, 100);
- // First, user enabled mode
- Condition[] userConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_TRUE, SOURCE_USER_ACTION)
- };
- mProviders.notifyConditions("package", msi, userConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
-
- // Second, app tries to disable it, but cannot
- Condition[] appConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_FALSE)
- };
- mProviders.notifyConditions("package", msi, appConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
- }
-
- @Test
- @EnableFlags(Flags.FLAG_MODES_UI)
- public void notifyConditions_appCanTakeoverUserEnablement() {
- ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
- mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
- mock(ServiceConnection.class), 33, 100);
- // First, user enabled mode
- Condition[] userConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_TRUE, SOURCE_USER_ACTION)
- };
- mProviders.notifyConditions("package", msi, userConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
-
- // Second, app now thinks the rule should be on due it its intelligence
- Condition[] appConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_TRUE)
- };
- mProviders.notifyConditions("package", msi, appConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(appConditions[0]));
-
- // Lastly, app can turn rule off when its intelligence think it should be off
- appConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_FALSE)
- };
- mProviders.notifyConditions("package", msi, appConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(appConditions[0]));
-
- verifyNoMoreInteractions(mCallback);
- }
-
- @Test
public void testRemoveDefaultFromConfig() {
final int userId = 0;
ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 14ad15e..643ee4a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -2458,6 +2458,74 @@
}
@Test
+ public void testBeepVolume_politeNotif_AvalancheStrategy_mixedNotif() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ // Trigger avalanche trigger intent
+ final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", false);
+ mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+
+ // Regular notification: should beep at 0% volume
+ NotificationRecord r = getBeepyNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ Mockito.reset(mRingtonePlayer);
+
+ // Conversation notification
+ mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
+ NotificationRecord r2 = getConversationNotificationRecord(mId, false /* insistent */,
+ false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+ true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+ "shortcut");
+
+ // Should beep at 100% volume
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ assertNotEquals(-1, r2.getLastAudiblyAlertedMs());
+ verifyBeepVolume(1.0f);
+
+ // Conversation notification on a different channel
+ mChannel = new NotificationChannel("test3", "test3", IMPORTANCE_DEFAULT);
+ NotificationRecord r3 = getConversationNotificationRecord(mId, false /* insistent */,
+ false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+ true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+ "shortcut");
+
+ // Should beep at 50% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+ assertNotEquals(-1, r3.getLastAudiblyAlertedMs());
+ verifyBeepVolume(0.5f);
+
+ // 2nd update should beep at 0% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
+
+ // Set important conversation
+ mChannel.setImportantConversation(true);
+ r3 = getConversationNotificationRecord(mId, false /* insistent */,
+ false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+ true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+ "shortcut");
+
+ // important conversation should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ verify(mAccessibilityService, times(5)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r3.getLastAudiblyAlertedMs());
+ }
+
+ @Test
public void testBeepVolume_politeNotif_Avalanche_exemptEmergency() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 60c4ac7..e70ed5f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -33,6 +33,8 @@
import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.service.notification.ZenModeConfig.XML_VERSION_MODES_API;
import static android.service.notification.ZenModeConfig.ZEN_TAG;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_NONE;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
@@ -524,7 +526,7 @@
rule.zenMode = INTERRUPTION_FILTER;
rule.modified = true;
rule.name = NAME;
- rule.snoozing = true;
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
rule.zenPolicy = POLICY;
@@ -546,7 +548,7 @@
ZenModeConfig.ZenRule parceled = new ZenModeConfig.ZenRule(parcel);
assertEquals(rule.pkg, parceled.pkg);
- assertEquals(rule.snoozing, parceled.snoozing);
+ assertEquals(rule.getConditionOverride(), parceled.getConditionOverride());
assertEquals(rule.enabler, parceled.enabler);
assertEquals(rule.component, parceled.component);
assertEquals(rule.configurationActivity, parceled.configurationActivity);
@@ -625,7 +627,7 @@
rule.zenMode = INTERRUPTION_FILTER;
rule.modified = true;
rule.name = NAME;
- rule.snoozing = true;
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
rule.zenPolicy = POLICY;
rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
@@ -662,7 +664,7 @@
assertEquals(rule.pkg, fromXml.pkg);
// always resets on reboot
- assertFalse(fromXml.snoozing);
+ assertEquals(OVERRIDE_NONE, fromXml.getConditionOverride());
//should all match original
assertEquals(rule.component, fromXml.component);
assertEquals(rule.configurationActivity, fromXml.configurationActivity);
@@ -1115,7 +1117,6 @@
rule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
rule.modified = true;
rule.name = "name";
- rule.snoozing = false;
rule.pkg = "b";
config.automaticRules.put("key", rule);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 9af0021..91eb2ed 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -158,7 +158,10 @@
RuleDiff.FIELD_ZEN_DEVICE_EFFECTS,
RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS));
}
- if (!(Flags.modesApi() && Flags.modesUi())) {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ exemptFields.add(RuleDiff.FIELD_SNOOZING); // Obsolete.
+ } else {
+ exemptFields.add(RuleDiff.FIELD_CONDITION_OVERRIDE);
exemptFields.add(RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS);
}
return exemptFields;
@@ -339,7 +342,7 @@
rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
rule.modified = false;
rule.name = "name";
- rule.snoozing = true;
+ rule.setConditionOverride(ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE);
rule.pkg = "a";
if (android.app.Flags.modesApi()) {
rule.allowManualInvocation = true;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 776a840..212e61e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -51,6 +51,7 @@
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.Condition.SOURCE_CONTEXT;
import static android.service.notification.Condition.SOURCE_SCHEDULE;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_FALSE;
@@ -61,6 +62,9 @@
import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_APP;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_ACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
@@ -2999,11 +3003,13 @@
assertWithMessage("Failure for origin " + origin.name())
.that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
assertWithMessage("Failure for origin " + origin.name())
- .that(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing)
- .isTrue();
+ .that(mZenModeHelper.mConfig.automaticRules
+ .get(activeRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_DEACTIVATE);
assertWithMessage("Failure for origin " + origin.name())
- .that(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing)
- .isFalse();
+ .that(mZenModeHelper.mConfig.automaticRules
+ .get(inactiveRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
}
}
@@ -3038,16 +3044,20 @@
assertWithMessage("Failure for origin " + origin.name()).that(
mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
assertWithMessage("Failure for origin " + origin.name()).that(
- config.automaticRules.get(activeRuleId).snoozing).isFalse();
+ config.automaticRules.get(activeRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
assertWithMessage("Failure for origin " + origin.name()).that(
- config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+ config.automaticRules.get(inactiveRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
} else {
assertWithMessage("Failure for origin " + origin.name()).that(
mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
assertWithMessage("Failure for origin " + origin.name()).that(
- config.automaticRules.get(activeRuleId).snoozing).isTrue();
+ config.automaticRules.get(activeRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_DEACTIVATE);
assertWithMessage("Failure for origin " + origin.name()).that(
- config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+ config.automaticRules.get(inactiveRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
}
}
}
@@ -4288,7 +4298,7 @@
rule.zenMode = INTERRUPTION_FILTER_ZR;
rule.modified = true;
rule.name = NAME;
- rule.snoozing = true;
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
rule.zenPolicy = POLICY;
@@ -5000,7 +5010,8 @@
mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, ZenModeConfig.ORIGIN_SYSTEM,
"", SYSTEM_UID);
- assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
+ assertEquals(OVERRIDE_NONE,
+ mZenModeHelper.mConfig.automaticRules.get(createdId).getConditionOverride());
}
@Test
@@ -5713,7 +5724,7 @@
assertThat(storedRule.isAutomaticActive()).isFalse();
assertThat(storedRule.isTrueOrUnknown()).isFalse();
assertThat(storedRule.condition).isNull();
- assertThat(storedRule.snoozing).isFalse();
+ assertThat(storedRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
}
@@ -6039,15 +6050,18 @@
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
ZEN_MODE_IMPORTANT_INTERRUPTIONS);
assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
- assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ORIGIN_APP, "test", "test", 0);
- assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isTrue();
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+ .isEqualTo(OVERRIDE_DEACTIVATE);
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
ZEN_MODE_ALARMS);
- assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
.isEqualTo(STATE_TRUE);
}
@@ -6451,6 +6465,160 @@
ORIGIN_UNKNOWN);
}
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualActivation_appliesOverride() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+
+ ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+ assertThat(zenRule.condition).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualActivationAndThenDeactivation_removesOverride() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+ ZenRule zenRule;
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+ assertThat(zenRule.condition).isNull();
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualDeactivation_appliesOverride() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, SYSTEM_UID);
+ ZenRule zenRuleOn = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRuleOn.isAutomaticActive()).isTrue();
+ assertThat(zenRuleOn.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRuleOn.condition).isNotNull();
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ ZenRule zenRuleOff = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRuleOff.isAutomaticActive()).isFalse();
+ assertThat(zenRuleOff.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+ assertThat(zenRuleOff.condition).isNotNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_ifManualActive_appCannotDeactivateBeforeActivating() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+ ZenRule zenRule;
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_ifManualInactive_appCannotReactivateBeforeDeactivating() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+ ZenRule zenRule;
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
index 59d5577..1493253 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.when;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.IVibrator;
import android.os.CombinedVibration;
@@ -32,11 +33,12 @@
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.SparseArray;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import com.android.server.LocalServices;
@@ -76,9 +78,10 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
+ Context context = ApplicationProvider.getApplicationContext();
mTestLooper = new TestLooper();
- mVibrationSettings = new VibrationSettings(
- InstrumentationRegistry.getContext(), new Handler(mTestLooper.getLooper()));
+ mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
+ new VibrationConfig(context.getResources()));
SparseArray<VibratorController> vibrators = new SparseArray<>();
vibrators.put(EMPTY_VIBRATOR_ID, createEmptyVibratorController(EMPTY_VIBRATOR_ID));
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index 9ebeaa8..4704691 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -50,10 +50,9 @@
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
@@ -71,12 +70,13 @@
import org.mockito.junit.MockitoRule;
public class VibrationScalerTest {
+ private static final float TOLERANCE = 1e-2f;
+ private static final int TEST_DEFAULT_AMPLITUDE = 255;
+ private static final float TEST_DEFAULT_SCALE_LEVEL_GAIN = 1.4f;
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private PowerManagerInternal mPowerManagerInternalMock;
@Mock private PackageManagerInternal mPackageManagerInternalMock;
@@ -96,6 +96,10 @@
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName("", ""));
+ when(mVibrationConfigMock.getDefaultVibrationAmplitude())
+ .thenReturn(TEST_DEFAULT_AMPLITUDE);
+ when(mVibrationConfigMock.getDefaultVibrationScaleLevelGain())
+ .thenReturn(TEST_DEFAULT_SCALE_LEVEL_GAIN);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
@@ -107,7 +111,7 @@
mVibrationSettings = new VibrationSettings(
mContextSpy, new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
- mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings);
+ mVibrationScaler = new VibrationScaler(mVibrationConfigMock, mVibrationSettings);
mVibrationSettings.onSystemReady();
}
@@ -147,33 +151,76 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
- public void testAdaptiveHapticsScale_withAdaptiveHapticsAvailable() {
- setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
- setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testGetScaleFactor_withLegacyScaling() {
+ // Default scale gain will be ignored.
+ when(mVibrationConfigMock.getDefaultVibrationScaleLevelGain()).thenReturn(1.4f);
+ mVibrationScaler = new VibrationScaler(mVibrationConfigMock, mVibrationSettings);
+ setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ assertEquals(1.4f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // VERY_HIGH
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
+ assertEquals(1.2f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // HIGH
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
+ assertEquals(1f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // NONE
+
+ setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
+ assertEquals(0.8f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // LOW
+
+ setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
+ assertEquals(0.6f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // VERY_LOW
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ // Vibration setting being bypassed will use default setting and not scale.
+ assertEquals(1f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // NONE
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED)
+ public void testGetScaleFactor_withScalingV2() {
+ // Test scale factors for a default gain of 1.4
+ when(mVibrationConfigMock.getDefaultVibrationScaleLevelGain()).thenReturn(1.4f);
+ mVibrationScaler = new VibrationScaler(mVibrationConfigMock, mVibrationSettings);
+
+ setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ assertEquals(1.95f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // VERY_HIGH
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
+ assertEquals(1.4f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // HIGH
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
+ assertEquals(1f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // NONE
+
+ setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
+ assertEquals(0.71f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // LOW
+
+ setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
+ assertEquals(0.51f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // VERY_LOW
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ // Vibration setting being bypassed will use default setting and not scale.
+ assertEquals(1f, mVibrationScaler.getScaleFactor(USAGE_TOUCH), TOLERANCE); // NONE
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void testAdaptiveHapticsScale_withAdaptiveHapticsAvailable() {
mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f);
mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f);
assertEquals(0.5f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH));
assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION));
-
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- // Vibration setting being bypassed will apply adaptive haptics scales.
assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @DisableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void testAdaptiveHapticsScale_flagDisabled_adaptiveHapticScaleAlwaysNone() {
- setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
- setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
-
mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f);
mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f);
@@ -233,7 +280,7 @@
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void scale_withVendorEffect_setsEffectStrengthBasedOnSettings() {
setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
@@ -269,13 +316,13 @@
StepSegment resolved = getFirstSegment(mVibrationScaler.scale(
VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE),
USAGE_RINGTONE));
- assertTrue(resolved.getAmplitude() > 0);
+ assertEquals(TEST_DEFAULT_AMPLITUDE / 255f, resolved.getAmplitude(), TOLERANCE);
resolved = getFirstSegment(mVibrationScaler.scale(
VibrationEffect.createWaveform(new long[]{10},
new int[]{VibrationEffect.DEFAULT_AMPLITUDE}, -1),
USAGE_RINGTONE));
- assertTrue(resolved.getAmplitude() > 0);
+ assertEquals(TEST_DEFAULT_AMPLITUDE / 255f, resolved.getAmplitude(), TOLERANCE);
}
@Test
@@ -330,7 +377,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void scale_withAdaptiveHaptics_scalesVibrationsCorrectly() {
setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
@@ -351,7 +398,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void scale_clearAdaptiveHapticsScales_clearsAllCachedScales() {
setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
@@ -373,7 +420,7 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void scale_removeAdaptiveHapticsScale_removesCachedScale() {
setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
@@ -395,7 +442,7 @@
}
@Test
- @RequiresFlagsEnabled({
+ @EnableFlags({
android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
})
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 72ef888..6f06050 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -119,6 +119,7 @@
USAGE_PHYSICAL_EMULATION,
USAGE_RINGTONE,
USAGE_TOUCH,
+ USAGE_IME_FEEDBACK
};
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -525,7 +526,7 @@
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
for (int usage : ALL_USAGES) {
- if (usage == USAGE_TOUCH) {
+ if (usage == USAGE_TOUCH || usage == USAGE_IME_FEEDBACK) {
assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
} else {
assertVibrationNotIgnoredForUsage(usage);
@@ -601,14 +602,14 @@
@Test
public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {
+ setKeyboardVibrationSettingsSupported(true);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0 /* OFF*/);
- setKeyboardVibrationSettingsSupported(true);
// Keyboard touch ignored.
assertVibrationIgnoredForAttributes(
new VibrationAttributes.Builder()
- .setUsage(USAGE_TOUCH)
+ .setUsage(USAGE_IME_FEEDBACK)
.setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build(),
Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -617,7 +618,7 @@
assertVibrationNotIgnoredForUsage(USAGE_TOUCH);
assertVibrationNotIgnoredForAttributes(
new VibrationAttributes.Builder()
- .setUsage(USAGE_TOUCH)
+ .setUsage(USAGE_IME_FEEDBACK)
.setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
.build());
@@ -625,9 +626,9 @@
@Test
public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {
+ setKeyboardVibrationSettingsSupported(true);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
- setKeyboardVibrationSettingsSupported(true);
// General touch ignored.
assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -635,16 +636,16 @@
// Keyboard touch not ignored.
assertVibrationNotIgnoredForAttributes(
new VibrationAttributes.Builder()
- .setUsage(USAGE_TOUCH)
+ .setUsage(USAGE_IME_FEEDBACK)
.setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build());
}
@Test
- public void shouldIgnoreVibration_notSupportKeyboardVibration_ignoresKeyboardTouchVibration() {
+ public void shouldIgnoreVibration_notSupportKeyboardVibration_followsTouchFeedbackSettings() {
+ setKeyboardVibrationSettingsSupported(false);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
- setKeyboardVibrationSettingsSupported(false);
// General touch ignored.
assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -652,7 +653,7 @@
// Keyboard touch ignored.
assertVibrationIgnoredForAttributes(
new VibrationAttributes.Builder()
- .setUsage(USAGE_TOUCH)
+ .setUsage(USAGE_IME_FEEDBACK)
.setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build(),
Vibration.Status.IGNORED_FOR_SETTINGS);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 3bd56de..0fbdce4 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -102,6 +102,8 @@
private static final String PACKAGE_NAME = "package";
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
private static final int TEST_RAMP_STEP_DURATION = 5;
+ private static final int TEST_DEFAULT_AMPLITUDE = 255;
+ private static final float TEST_DEFAULT_SCALE_LEVEL_GAIN = 1.4f;
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
@@ -133,6 +135,10 @@
when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt()))
.thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
when(mVibrationConfigMock.getRampStepDurationMs()).thenReturn(TEST_RAMP_STEP_DURATION);
+ when(mVibrationConfigMock.getDefaultVibrationAmplitude())
+ .thenReturn(TEST_DEFAULT_AMPLITUDE);
+ when(mVibrationConfigMock.getDefaultVibrationScaleLevelGain())
+ .thenReturn(TEST_DEFAULT_SCALE_LEVEL_GAIN);
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName("", ""));
doAnswer(answer -> {
@@ -146,7 +152,7 @@
Context context = InstrumentationRegistry.getContext();
mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
mVibrationConfigMock);
- mVibrationScaler = new VibrationScaler(context, mVibrationSettings);
+ mVibrationScaler = new VibrationScaler(mVibrationConfigMock, mVibrationSettings);
mockVibrators(VIBRATOR_ID);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index c496bbb..79e272b 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -37,6 +37,7 @@
import static org.mockito.Mockito.when;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.frameworks.vibrator.ScaleParam;
import android.frameworks.vibrator.VibrationParam;
@@ -46,6 +47,7 @@
import android.os.Process;
import android.os.test.TestLooper;
import android.os.vibrator.Flags;
+import android.os.vibrator.VibrationConfig;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -97,8 +99,9 @@
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
TestLooper testLooper = new TestLooper();
- mVibrationSettings = new VibrationSettings(
- ApplicationProvider.getApplicationContext(), new Handler(testLooper.getLooper()));
+ Context context = ApplicationProvider.getApplicationContext();
+ mVibrationSettings = new VibrationSettings(context, new Handler(testLooper.getLooper()),
+ new VibrationConfig(context.getResources()));
mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper());
mVibratorControlService = new VibratorControlService(
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index e2524a2..ddadbc4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -115,6 +115,17 @@
}
@Test
+ public void testPrimaryDisplayUnchanged_whenWindowingModeAlreadySet_NoFreeformSupport() {
+ mPrimaryDisplay.getDefaultTaskDisplayArea().setWindowingMode(
+ WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+
+ mDisplayWindowSettings.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+ assertEquals(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW,
+ mPrimaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
+ }
+
+ @Test
public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_NoDesktopMode() {
mWm.mAtmService.mSupportsFreeformWindowManagement = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index ffaa2d8..400fe8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -45,6 +46,12 @@
import java.util.function.Supplier;
+/**
+ * Test class for {@link Letterbox}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:LetterboxTest
+ */
@SmallTest
@Presubmit
public class LetterboxTest {
@@ -53,21 +60,21 @@
SurfaceControlMocker mSurfaces;
SurfaceControl.Transaction mTransaction;
- private boolean mAreCornersRounded = false;
- private int mColor = Color.BLACK;
- private boolean mHasWallpaperBackground = false;
- private int mBlurRadius = 0;
- private float mDarkScrimAlpha = 0.5f;
private SurfaceControl mParentSurface = mock(SurfaceControl.class);
+ private AppCompatLetterboxOverrides mLetterboxOverrides;
@Before
public void setUp() throws Exception {
mSurfaces = new SurfaceControlMocker();
+ mLetterboxOverrides = mock(AppCompatLetterboxOverrides.class);
+ doReturn(false).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
+ doReturn(Color.valueOf(Color.BLACK)).when(mLetterboxOverrides)
+ .getLetterboxBackgroundColor();
+ doReturn(false).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
+ doReturn(0).when(mLetterboxOverrides).getLetterboxWallpaperBlurRadiusPx();
+ doReturn(0.5f).when(mLetterboxOverrides).getLetterboxWallpaperDarkScrimAlpha();
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
- () -> mAreCornersRounded, () -> Color.valueOf(mColor),
- () -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha,
- mock(AppCompatReachabilityPolicy.class),
- () -> mParentSurface);
+ mock(AppCompatReachabilityPolicy.class), mLetterboxOverrides, () -> mParentSurface);
mTransaction = spy(StubTransaction.class);
}
@@ -183,7 +190,8 @@
verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 0, 0});
- mColor = Color.GREEN;
+ doReturn(Color.valueOf(Color.GREEN)).when(mLetterboxOverrides)
+ .getLetterboxBackgroundColor();
assertTrue(mLetterbox.needsApplySurfaceChanges());
@@ -200,12 +208,12 @@
verify(mTransaction).setAlpha(mSurfaces.top, 1.0f);
assertFalse(mLetterbox.needsApplySurfaceChanges());
- mHasWallpaperBackground = true;
+ doReturn(true).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
assertTrue(mLetterbox.needsApplySurfaceChanges());
applySurfaceChanges();
- verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
+ verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, /* alpha */ 0.5f);
}
@Test
@@ -234,7 +242,7 @@
@Test
public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
- mAreCornersRounded = true;
+ doReturn(true).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
applySurfaceChanges();
@@ -243,7 +251,7 @@
@Test
public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
- mHasWallpaperBackground = true;
+ doReturn(true).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
applySurfaceChanges();
@@ -252,7 +260,7 @@
@Test
public void testNotIntersectsOrFullyContains_cornersRounded() {
- mAreCornersRounded = true;
+ doReturn(true).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
applySurfaceChanges();
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 695068a..04997f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -123,7 +123,7 @@
// Do not apply crop if taskbar is collapsed
taskbar.setFrame(TASKBAR_COLLAPSED_BOUNDS);
- assertNull(mController.getExpandedTaskbarOrNull(mainWindow));
+ assertNull(AppCompatUtils.getExpandedTaskbarOrNull(mainWindow));
mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4,
SCREEN_WIDTH - SCREEN_WIDTH / 4, SCREEN_HEIGHT - SCREEN_HEIGHT / 4);
@@ -145,7 +145,7 @@
// Apply crop if taskbar is expanded
taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
- assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
+ assertNotNull(AppCompatUtils.getExpandedTaskbarOrNull(mainWindow));
mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, 0, SCREEN_WIDTH - SCREEN_WIDTH / 4,
SCREEN_HEIGHT);
@@ -169,7 +169,7 @@
// Apply crop if taskbar is expanded
taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
- assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
+ assertNotNull(AppCompatUtils.getExpandedTaskbarOrNull(mainWindow));
// With SizeCompat scaling
doReturn(true).when(mActivity).inSizeCompatMode();
mainWindow.mInvGlobalScale = scaling;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index aa997ac..3148808 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -247,9 +247,9 @@
.build();
mTask.addChild(translucentActivity);
- spyOn(translucentActivity.mLetterboxUiController);
- doReturn(true).when(translucentActivity.mLetterboxUiController)
- .shouldShowLetterboxUi(any());
+ spyOn(translucentActivity.mAppCompatController.getAppCompatLetterboxPolicy());
+ doReturn(true).when(translucentActivity.mAppCompatController
+ .getAppCompatLetterboxPolicy()).shouldShowLetterboxUi(any());
addWindowToActivity(translucentActivity);
translucentActivity.mRootWindowContainer.performSurfacePlacement();
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index ad6db2d..e657d7f 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -254,7 +254,6 @@
*/
public static final String KEY_PROVISION_SATELLITE_TOKENS = "provision_satellite";
-
/**
* The request was successfully processed.
*/
@@ -2643,7 +2642,7 @@
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public void requestProvisionSubscriberIds(@NonNull @CallbackExecutor Executor executor,
- @NonNull OutcomeReceiver<List<ProvisionSubscriberId>, SatelliteException> callback) {
+ @NonNull OutcomeReceiver<List<SatelliteSubscriberInfo>, SatelliteException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
@@ -2655,10 +2654,10 @@
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == SATELLITE_RESULT_SUCCESS) {
if (resultData.containsKey(KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN)) {
- List<ProvisionSubscriberId> list =
+ List<SatelliteSubscriberInfo> list =
resultData.getParcelableArrayList(
KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN,
- ProvisionSubscriberId.class);
+ SatelliteSubscriberInfo.class);
executor.execute(() -> Binder.withCleanCallingIdentity(() ->
callback.onResult(list)));
} else {
@@ -2743,9 +2742,9 @@
}
/**
- * Deliver the list of provisioned satellite subscriber ids.
+ * Deliver the list of provisioned satellite subscriber infos.
*
- * @param list List of ProvisionSubscriberId.
+ * @param list The list of provisioned satellite subscriber infos.
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
*
@@ -2754,7 +2753,7 @@
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public void provisionSatellite(@NonNull List<ProvisionSubscriberId> list,
+ public void provisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
diff --git a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl
similarity index 94%
rename from telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl
rename to telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl
index fe46db8..992c9ae 100644
--- a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl
@@ -16,4 +16,4 @@
package android.telephony.satellite;
-parcelable ProvisionSubscriberId;
+parcelable SatelliteSubscriberInfo;
diff --git a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
similarity index 83%
rename from telephony/java/android/telephony/satellite/ProvisionSubscriberId.java
rename to telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
index 3e6f743..f26219b 100644
--- a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -26,7 +26,7 @@
import java.util.Objects;
/**
- * ProvisionSubscriberId
+ * SatelliteSubscriberInfo
*
* Satellite Gateway client will use these subscriber ids to register with satellite gateway service
* which identify user subscription with unique subscriber ids. These subscriber ids can be any
@@ -35,7 +35,7 @@
* @hide
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public final class ProvisionSubscriberId implements Parcelable {
+public final class SatelliteSubscriberInfo implements Parcelable {
/** provision subscriberId */
@NonNull
private String mSubscriberId;
@@ -49,14 +49,14 @@
/**
* @hide
*/
- public ProvisionSubscriberId(@NonNull String subscriberId, @NonNull int carrierId,
+ public SatelliteSubscriberInfo(@NonNull String subscriberId, @NonNull int carrierId,
@NonNull String niddApn) {
this.mCarrierId = carrierId;
this.mSubscriberId = subscriberId;
this.mNiddApn = niddApn;
}
- private ProvisionSubscriberId(Parcel in) {
+ private SatelliteSubscriberInfo(Parcel in) {
readFromParcel(in);
}
@@ -72,16 +72,16 @@
}
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static final @android.annotation.NonNull Creator<ProvisionSubscriberId> CREATOR =
- new Creator<ProvisionSubscriberId>() {
+ public static final @android.annotation.NonNull Creator<SatelliteSubscriberInfo> CREATOR =
+ new Creator<SatelliteSubscriberInfo>() {
@Override
- public ProvisionSubscriberId createFromParcel(Parcel in) {
- return new ProvisionSubscriberId(in);
+ public SatelliteSubscriberInfo createFromParcel(Parcel in) {
+ return new SatelliteSubscriberInfo(in);
}
@Override
- public ProvisionSubscriberId[] newArray(int size) {
- return new ProvisionSubscriberId[size];
+ public SatelliteSubscriberInfo[] newArray(int size) {
+ return new SatelliteSubscriberInfo[size];
}
};
@@ -148,7 +148,7 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- ProvisionSubscriberId that = (ProvisionSubscriberId) o;
+ SatelliteSubscriberInfo that = (SatelliteSubscriberInfo) o;
return mSubscriberId.equals(that.mSubscriberId) && mCarrierId
== that.mCarrierId && mNiddApn.equals(that.mNiddApn);
}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl
similarity index 95%
rename from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
rename to telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl
index 460de8c..fb44f87 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl
@@ -19,7 +19,7 @@
/**
* {@hide}
*/
-parcelable ProvisionSubscriberId {
+parcelable SatelliteSubscriberInfo {
/** provision subscriberId */
String subscriberId;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2f8e957..8919703 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -78,7 +78,7 @@
import android.telephony.satellite.NtnSignalStrength;
import android.telephony.satellite.SatelliteCapabilities;
import android.telephony.satellite.SatelliteDatagram;
-import android.telephony.satellite.ProvisionSubscriberId;
+import android.telephony.satellite.SatelliteSubscriberInfo;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
@@ -3428,13 +3428,13 @@
void requestIsProvisioned(in String satelliteSubscriberId, in ResultReceiver result);
/**
- * Deliver the list of provisioned satellite subscriber ids.
+ * Deliver the list of provisioned satellite subscriber infos.
*
- * @param list List of provisioned satellite subscriber ids.
+ * @param list The list of provisioned satellite subscriber infos.
* @param result The result receiver that returns whether deliver success or fail.
* @hide
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void provisionSatellite(in List<ProvisionSubscriberId> list, in ResultReceiver result);
+ void provisionSatellite(in List<SatelliteSubscriberInfo> list, in ResultReceiver result);
}
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index 827ff4f..ad98e47 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -24,6 +24,7 @@
"flickerlib-parsers",
"perfetto_trace_java_protos",
"flickerlib-trace_processor_shell",
+ "ravenwood-junit",
],
java_resource_dirs: ["res"],
certificate: "platform",
@@ -39,6 +40,7 @@
"platform-test-annotations",
],
srcs: [
+ "src/com/android/internal/graphics/ColorUtilsTest.java",
"src/com/android/internal/util/ParcellingTests.java",
],
auto_gen_config: true,
diff --git a/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java b/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
index d0bb8e3..38a22f2 100644
--- a/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
+++ b/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
@@ -19,14 +19,19 @@
import static org.junit.Assert.assertTrue;
import android.graphics.Color;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
@SmallTest
public class ColorUtilsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void calculateMinimumBackgroundAlpha_satisfiestContrast() {
diff --git a/tests/Internal/src/com/android/internal/util/ParcellingTests.java b/tests/Internal/src/com/android/internal/util/ParcellingTests.java
index 65a3436..fb63422 100644
--- a/tests/Internal/src/com/android/internal/util/ParcellingTests.java
+++ b/tests/Internal/src/com/android/internal/util/ParcellingTests.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -26,6 +27,7 @@
import com.android.internal.util.Parcelling.BuiltIn.ForInstant;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -38,6 +40,9 @@
@RunWith(JUnit4.class)
public class ParcellingTests {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private Parcel mParcel = Parcel.obtain();
@Test
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 3f9016b..f43cf52 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -113,6 +113,7 @@
"io/ZipArchive.cpp",
"link/AutoVersioner.cpp",
"link/FeatureFlagsFilter.cpp",
+ "link/FlagDisabledResourceRemover.cpp",
"link/ManifestFixer.cpp",
"link/NoDefaultResourceRemover.cpp",
"link/PrivateAttributeMover.cpp",
@@ -189,6 +190,8 @@
"integration-tests/CommandTests/**/*",
"integration-tests/ConvertTest/**/*",
"integration-tests/DumpTest/**/*",
+ ":resource-flagging-test-app-apk",
+ ":resource-flagging-test-app-r-java",
],
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 9444dd9..1c85e9f 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -690,9 +690,7 @@
resource_format = item_iter->second.format;
}
- // Don't bother parsing the item if it is behind a disabled flag
- if (out_resource->flag_status != FlagStatus::Disabled &&
- !ParseItem(parser, out_resource, resource_format)) {
+ if (!ParseItem(parser, out_resource, resource_format)) {
return false;
}
return true;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 2e6ad13..b59b165 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -69,13 +69,8 @@
return TestParse(str, ConfigDescription{});
}
- ::testing::AssertionResult TestParse(StringPiece str, ResourceParserOptions parserOptions) {
- return TestParse(str, ConfigDescription{}, parserOptions);
- }
-
- ::testing::AssertionResult TestParse(
- StringPiece str, const ConfigDescription& config,
- ResourceParserOptions parserOptions = ResourceParserOptions()) {
+ ::testing::AssertionResult TestParse(StringPiece str, const ConfigDescription& config) {
+ ResourceParserOptions parserOptions;
ResourceParser parser(context_->GetDiagnostics(), &table_, android::Source{"test"}, config,
parserOptions);
@@ -247,19 +242,6 @@
EXPECT_FALSE(TestParse(R"(<string name="foo4" translatable="yes">Translate</string>)"));
}
-TEST_F(ResourceParserTest, ParseStringBehindDisabledFlag) {
- FeatureFlagProperties flag_properties(true, false);
- ResourceParserOptions options;
- options.feature_flag_values = {{"falseFlag", flag_properties}};
- ASSERT_TRUE(TestParse(
- R"(<string name="foo" android:featureFlag="falseFlag"
- xmlns:android="http://schemas.android.com/apk/res/android">foo</string>)",
- options));
-
- String* str = test::GetValue<String>(&table_, "string/foo");
- ASSERT_THAT(str, IsNull());
-}
-
TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
std::string input = R"(
<string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 642a561..56f5288 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -57,6 +57,7 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/FeatureFlagsFilter.h"
+#include "link/FlagDisabledResourceRemover.h"
#include "link/Linkers.h"
#include "link/ManifestFixer.h"
#include "link/NoDefaultResourceRemover.h"
@@ -1840,11 +1841,57 @@
return validate(attr->value);
}
+ class FlagDisabledStringVisitor : public DescendingValueVisitor {
+ public:
+ using DescendingValueVisitor::Visit;
+
+ explicit FlagDisabledStringVisitor(android::StringPool& string_pool)
+ : string_pool_(string_pool) {
+ }
+
+ void Visit(RawString* value) override {
+ value->value = string_pool_.MakeRef("");
+ }
+
+ void Visit(String* value) override {
+ value->value = string_pool_.MakeRef("");
+ }
+
+ void Visit(StyledString* value) override {
+ value->value = string_pool_.MakeRef(android::StyleString{{""}, {}});
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FlagDisabledStringVisitor);
+ android::StringPool& string_pool_;
+ };
+
// Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
// to the IArchiveWriter.
bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
ResourceTable* table) {
TRACE_CALL();
+
+ FlagDisabledStringVisitor visitor(table->string_pool);
+
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ for (auto& config_value : entry->values) {
+ if (config_value->flag_status == FlagStatus::Disabled) {
+ config_value->value->Accept(&visitor);
+ }
+ }
+ }
+ }
+ }
+
+ if (!FlagDisabledResourceRemover{}.Consume(context_, table)) {
+ context_->GetDiagnostics()->Error(android::DiagMessage()
+ << "failed removing resources behind disabled flags");
+ return 1;
+ }
+
const bool keep_raw_values = (context_->GetPackageType() == PackageType::kStaticLib)
|| options_.keep_raw_values;
bool result = FlattenXml(context_, *manifest, kAndroidManifestPath, keep_raw_values,
@@ -2331,6 +2378,12 @@
return 1;
};
+ if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
+ if (!GenerateJavaClasses()) {
+ return 1;
+ }
+ }
+
if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
return 1;
}
@@ -2339,12 +2392,6 @@
return 1;
}
- if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
- if (!GenerateJavaClasses()) {
- return 1;
- }
- }
-
if (!WriteProguardFile(options_.generate_proguard_rules_path, proguard_keep_set)) {
return 1;
}
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
new file mode 100644
index 0000000..5932271
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
@@ -0,0 +1,81 @@
+// Copyright (C) 2024 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_android_resources",
+}
+
+genrule {
+ name: "resource-flagging-test-app-compile",
+ tools: ["aapt2"],
+ srcs: [
+ "res/values/bools.xml",
+ "res/values/bools2.xml",
+ "res/values/strings.xml",
+ ],
+ out: [
+ "values_bools.arsc.flat",
+ "values_bools2.arsc.flat",
+ "values_strings.arsc.flat",
+ ],
+ cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
+ "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
+}
+
+genrule {
+ name: "resource-flagging-test-app-apk",
+ tools: ["aapt2"],
+ // The first input file in the list must be the manifest
+ srcs: [
+ "AndroidManifest.xml",
+ ":resource-flagging-test-app-compile",
+ ],
+ out: [
+ "resapp.apk",
+ ],
+ cmd: "$(location aapt2) link -o $(out) --manifest $(in)",
+}
+
+genrule {
+ name: "resource-flagging-test-app-r-java",
+ tools: ["aapt2"],
+ // The first input file in the list must be the manifest
+ srcs: [
+ "AndroidManifest.xml",
+ ":resource-flagging-test-app-compile",
+ ],
+ out: [
+ "resource-flagging-java/com/android/intenal/flaggedresources/R.java",
+ ],
+ cmd: "$(location aapt2) link -o $(genDir)/resapp.apk --java $(genDir)/resource-flagging-java --manifest $(in)",
+}
+
+java_genrule {
+ name: "resource-flagging-test-app-apk-as-resource",
+ srcs: [
+ ":resource-flagging-test-app-apk",
+ ],
+ out: ["apks_as_resources.res.zip"],
+ tools: ["soong_zip"],
+
+ cmd: "mkdir -p $(genDir)/res/raw && " +
+ "cp $(in) $(genDir)/res/raw/$$(basename $(in)) && " +
+ "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
+}
diff --git a/core/tests/resourceflaggingtests/TestAppAndroidManifest.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/AndroidManifest.xml
similarity index 100%
rename from core/tests/resourceflaggingtests/TestAppAndroidManifest.xml
rename to tools/aapt2/integration-tests/FlaggedResourcesTest/AndroidManifest.xml
diff --git a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
similarity index 82%
rename from core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml
rename to tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
index 8d01465..3e094fb 100644
--- a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
@@ -7,4 +7,6 @@
<bool name="res2" android:featureFlag="test.package.trueFlag">true</bool>
<bool name="res3">false</bool>
+
+ <bool name="res4" android:featureFlag="test.package.falseFlag">true</bool>
</resources>
\ No newline at end of file
diff --git a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools2.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools2.xml
similarity index 100%
rename from core/tests/resourceflaggingtests/flagged_resources_res/values/bools2.xml
rename to tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools2.xml
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
new file mode 100644
index 0000000..5c0fca1
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <string name="str">plain string</string>
+
+ <string name="str1" android:featureFlag="test.package.falseFlag">DONTFIND</string>
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/link/FlagDisabledResourceRemover.cpp b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
new file mode 100644
index 0000000..e3289e2
--- /dev/null
+++ b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#include "link/FlagDisabledResourceRemover.h"
+
+#include <algorithm>
+
+#include "ResourceTable.h"
+
+using android::ConfigDescription;
+
+namespace aapt {
+
+static bool KeepResourceEntry(const std::unique_ptr<ResourceEntry>& entry) {
+ if (entry->values.empty()) {
+ return true;
+ }
+ const auto end_iter = entry->values.end();
+ const auto remove_iter =
+ std::stable_partition(entry->values.begin(), end_iter,
+ [](const std::unique_ptr<ResourceConfigValue>& value) -> bool {
+ return value->flag_status != FlagStatus::Disabled;
+ });
+
+ bool keep = remove_iter != entry->values.begin();
+
+ entry->values.erase(remove_iter, end_iter);
+ return keep;
+}
+
+bool FlagDisabledResourceRemover::Consume(IAaptContext* context, ResourceTable* table) {
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ const auto end_iter = type->entries.end();
+ const auto remove_iter = std::stable_partition(
+ type->entries.begin(), end_iter, [](const std::unique_ptr<ResourceEntry>& entry) -> bool {
+ return KeepResourceEntry(entry);
+ });
+
+ type->entries.erase(remove_iter, end_iter);
+ }
+ }
+ return true;
+}
+
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/link/FlagDisabledResourceRemover.h b/tools/aapt2/link/FlagDisabledResourceRemover.h
new file mode 100644
index 0000000..2db2cb4
--- /dev/null
+++ b/tools/aapt2/link/FlagDisabledResourceRemover.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#pragma once
+
+#include "android-base/macros.h"
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+// Removes any resource that are behind disabled flags.
+class FlagDisabledResourceRemover : public IResourceTableConsumer {
+ public:
+ FlagDisabledResourceRemover() = default;
+
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FlagDisabledResourceRemover);
+};
+
+} // namespace aapt
diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
new file mode 100644
index 0000000..c901b58
--- /dev/null
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "LoadedApk.h"
+#include "cmd/Dump.h"
+#include "io/StringStream.h"
+#include "test/Test.h"
+#include "text/Printer.h"
+
+using ::aapt::io::StringOutputStream;
+using ::aapt::text::Printer;
+using testing::Eq;
+using testing::Ne;
+
+namespace aapt {
+
+using FlaggedResourcesTest = CommandTestFixture;
+
+static android::NoOpDiagnostics noop_diag;
+
+void DumpStringPoolToString(LoadedApk* loaded_apk, std::string* output) {
+ StringOutputStream output_stream(output);
+ Printer printer(&output_stream);
+
+ DumpStringsCommand command(&printer, &noop_diag);
+ ASSERT_EQ(command.Dump(loaded_apk), 0);
+ output_stream.Flush();
+}
+
+void DumpResourceTableToString(LoadedApk* loaded_apk, std::string* output) {
+ StringOutputStream output_stream(output);
+ Printer printer(&output_stream);
+
+ DumpTableCommand command(&printer, &noop_diag);
+ ASSERT_EQ(command.Dump(loaded_apk), 0);
+ output_stream.Flush();
+}
+
+void DumpChunksToString(LoadedApk* loaded_apk, std::string* output) {
+ StringOutputStream output_stream(output);
+ Printer printer(&output_stream);
+
+ DumpChunks command(&printer, &noop_diag);
+ ASSERT_EQ(command.Dump(loaded_apk), 0);
+ output_stream.Flush();
+}
+
+TEST_F(FlaggedResourcesTest, DisabledStringRemovedFromPool) {
+ auto apk_path = file::BuildPath({android::base::GetExecutableDirectory(), "resapp.apk"});
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
+
+ std::string output;
+ DumpStringPoolToString(loaded_apk.get(), &output);
+
+ std::string excluded = "DONTFIND";
+ ASSERT_EQ(output.find(excluded), std::string::npos);
+}
+
+TEST_F(FlaggedResourcesTest, DisabledResourcesRemovedFromTable) {
+ auto apk_path = file::BuildPath({android::base::GetExecutableDirectory(), "resapp.apk"});
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
+
+ std::string output;
+ DumpResourceTableToString(loaded_apk.get(), &output);
+}
+
+TEST_F(FlaggedResourcesTest, DisabledResourcesRemovedFromTableChunks) {
+ auto apk_path = file::BuildPath({android::base::GetExecutableDirectory(), "resapp.apk"});
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
+
+ std::string output;
+ DumpChunksToString(loaded_apk.get(), &output);
+
+ ASSERT_EQ(output.find("res4"), std::string::npos);
+ ASSERT_EQ(output.find("str1"), std::string::npos);
+}
+
+TEST_F(FlaggedResourcesTest, DisabledResourcesInRJava) {
+ auto r_path = file::BuildPath({android::base::GetExecutableDirectory(), "resource-flagging-java",
+ "com", "android", "intenal", "flaggedresources", "R.java"});
+ std::string r_contents;
+ ::android::base::ReadFileToString(r_path, &r_contents);
+
+ ASSERT_NE(r_contents.find("public static final int res4"), std::string::npos);
+ ASSERT_NE(r_contents.find("public static final int str1"), std::string::npos);
+}
+
+} // namespace aapt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 5dde265..36bfbef 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -58,17 +58,19 @@
// Dump the classes, if specified.
options.inputJarDumpFile.ifSet {
- PrintWriter(it).use { pw -> allClasses.dump(pw) }
- log.i("Dump file created at $it")
+ log.iTime("Dump file created at $it") {
+ PrintWriter(it).use { pw -> allClasses.dump(pw) }
+ }
}
options.inputJarAsKeepAllFile.ifSet {
- PrintWriter(it).use {
- pw -> allClasses.forEach {
- classNode -> printAsTextPolicy(pw, classNode)
+ log.iTime("Dump file created at $it") {
+ PrintWriter(it).use { pw ->
+ allClasses.forEach { classNode ->
+ printAsTextPolicy(pw, classNode)
+ }
}
}
- log.i("Dump file created at $it")
}
// Build the filters.
@@ -91,16 +93,18 @@
// Dump statistics, if specified.
options.statsFile.ifSet {
- PrintWriter(it).use { pw -> stats.dumpOverview(pw) }
- log.i("Dump file created at $it")
+ log.iTime("Dump file created at $it") {
+ PrintWriter(it).use { pw -> stats.dumpOverview(pw) }
+ }
}
options.apiListFile.ifSet {
- PrintWriter(it).use { pw ->
- // TODO, when dumping a jar that's not framework-minus-apex.jar, we need to feed
- // framework-minus-apex.jar so that we can dump inherited methods from it.
- ApiDumper(pw, allClasses, null, filter).dump()
+ log.iTime("API list file created at $it") {
+ PrintWriter(it).use { pw ->
+ // TODO, when dumping a jar that's not framework-minus-apex.jar, we need to feed
+ // framework-minus-apex.jar so that we can dump inherited methods from it.
+ ApiDumper(pw, allClasses, null, filter).dump()
+ }
}
- log.i("API list file created at $it")
}
}
@@ -221,47 +225,48 @@
log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar)
log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
- val start = System.currentTimeMillis()
+ log.iTime("Transforming jar") {
+ val packageRedirector = PackageRedirectRemapper(options.packageRedirects)
- val packageRedirector = PackageRedirectRemapper(options.packageRedirects)
+ var itemIndex = 0
+ var numItemsProcessed = 0
+ var numItems = -1 // == Unknown
- var itemIndex = 0
- var numItemsProcessed = 0
- var numItems = -1 // == Unknown
+ log.withIndent {
+ // Open the input jar file and process each entry.
+ ZipFile(inJar).use { inZip ->
- log.withIndent {
- // Open the input jar file and process each entry.
- ZipFile(inJar).use { inZip ->
+ numItems = inZip.size()
+ val shardStart = numItems * shard / numShards
+ val shardNextStart = numItems * (shard + 1) / numShards
- numItems = inZip.size()
- val shardStart = numItems * shard / numShards
- val shardNextStart = numItems * (shard + 1) / numShards
-
- maybeWithZipOutputStream(outStubJar) { stubOutStream ->
- maybeWithZipOutputStream(outImplJar) { implOutStream ->
- val inEntries = inZip.entries()
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
- val inShard = (shardStart <= itemIndex) && (itemIndex < shardNextStart)
- itemIndex++
- if (!inShard) {
- continue
- }
- convertSingleEntry(inZip, entry, stubOutStream, implOutStream,
+ maybeWithZipOutputStream(outStubJar) { stubOutStream ->
+ maybeWithZipOutputStream(outImplJar) { implOutStream ->
+ val inEntries = inZip.entries()
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
+ val inShard = (shardStart <= itemIndex)
+ && (itemIndex < shardNextStart)
+ itemIndex++
+ if (!inShard) {
+ continue
+ }
+ convertSingleEntry(
+ inZip, entry, stubOutStream, implOutStream,
filter, packageRedirector, remapper,
- enableChecker, classes, errors, stats)
- numItemsProcessed++
+ enableChecker, classes, errors, stats
+ )
+ numItemsProcessed++
+ }
+ log.i("Converted all entries.")
}
- log.i("Converted all entries.")
}
+ outStubJar?.let { log.i("Created stub: $it") }
+ outImplJar?.let { log.i("Created impl: $it") }
}
- outStubJar?.let { log.i("Created stub: $it") }
- outImplJar?.let { log.i("Created impl: $it") }
}
+ log.i("%d / %d item(s) processed.", numItemsProcessed, numItems)
}
- val end = System.currentTimeMillis()
- log.i("Done transforming the jar in %.1f second(s). %d / %d item(s) processed.",
- (end - start) / 1000.0, numItemsProcessed, numItems)
}
private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
index 18065ba..ee4a06f 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
@@ -185,6 +185,16 @@
println(LogLevel.Debug, format, *args)
}
+ inline fun <T> iTime(message: String, block: () -> T): T {
+ val start = System.currentTimeMillis()
+ val ret = block()
+ val end = System.currentTimeMillis()
+
+ log.i("%s: took %.1f second(s).", message, (end - start) / 1000.0)
+
+ return ret
+ }
+
inline fun forVerbose(block: () -> Unit) {
if (isEnabled(LogLevel.Verbose)) {
block()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
index 92906a7..2607df6 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
@@ -184,49 +184,50 @@
* Load all the classes, without code.
*/
fun loadClassStructures(inJar: String): ClassNodes {
- log.i("Reading class structure from $inJar ...")
- val start = System.currentTimeMillis()
+ log.iTime("Reading class structure from $inJar") {
+ val allClasses = ClassNodes()
- val allClasses = ClassNodes()
+ log.withIndent {
+ ZipFile(inJar).use { inZip ->
+ val inEntries = inZip.entries()
- log.withIndent {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
-
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- if (entry.name.endsWith(".class")) {
- val cr = ClassReader(bis)
- val cn = ClassNode()
- cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
- or ClassReader.SKIP_FRAMES)
- if (!allClasses.addClass(cn)) {
- log.w("Duplicate class found: ${cn.name}")
- }
- } else if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw InvalidJarFileException(
- "$inJar is not a desktop jar file. It contains a *.dex file.")
- } else {
- // Unknown file type. Skip.
- while (bis.available() > 0) {
- bis.skip((1024 * 1024).toLong())
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ if (entry.name.endsWith(".class")) {
+ val cr = ClassReader(bis)
+ val cn = ClassNode()
+ cr.accept(
+ cn, ClassReader.SKIP_CODE
+ or ClassReader.SKIP_DEBUG
+ or ClassReader.SKIP_FRAMES
+ )
+ if (!allClasses.addClass(cn)) {
+ log.w("Duplicate class found: ${cn.name}")
+ }
+ } else if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw InvalidJarFileException(
+ "$inJar is not a desktop jar file."
+ + " It contains a *.dex file."
+ )
+ } else {
+ // Unknown file type. Skip.
+ while (bis.available() > 0) {
+ bis.skip((1024 * 1024).toLong())
+ }
}
}
}
}
}
+ if (allClasses.size == 0) {
+ log.w("$inJar contains no *.class files.")
+ }
+ return allClasses
}
- if (allClasses.size == 0) {
- log.w("$inJar contains no *.class files.")
- }
-
- val end = System.currentTimeMillis()
- log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
- return allClasses
}
}
}
\ No newline at end of file