diff options
386 files changed, 11571 insertions, 8531 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index deb6f1393b0a..1ae9ada41338 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -789,7 +789,6 @@ java_aconfig_library { min_sdk_version: "30", apex_available: [ "//apex_available:platform", - "com.android.healthfitness", "com.android.permission", "com.android.nfcservices", ], diff --git a/apct-tests/perftests/tracing/Android.bp b/apct-tests/perftests/tracing/Android.bp index 08e365be514a..8814216644d7 100644 --- a/apct-tests/perftests/tracing/Android.bp +++ b/apct-tests/perftests/tracing/Android.bp @@ -22,6 +22,7 @@ android_test { "apct-perftests-utils", "collector-device-lib", "platform-test-annotations", + "perfetto_trace_java_protos", ], test_suites: [ "device-tests", diff --git a/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java index f4759b8bd35c..7ef8c53d1d62 100644 --- a/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java +++ b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java @@ -17,10 +17,12 @@ package com.android.internal.protolog; import android.app.Activity; import android.os.Bundle; +import android.os.ServiceManager.ServiceNotFoundException; import android.perftests.utils.Stats; import androidx.test.InstrumentationRegistry; +import com.android.internal.protolog.common.IProtoLog; import com.android.internal.protolog.common.IProtoLogGroup; import com.android.internal.protolog.common.LogLevel; @@ -31,6 +33,8 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import perfetto.protos.ProtologCommon; + import java.util.ArrayList; import java.util.Collection; @@ -65,24 +69,48 @@ public class ProtoLogPerfTest { }; } + private IProtoLog mProcessedProtoLogger; + private static final String MOCK_TEST_FILE_PATH = "mock/file/path"; + private static final perfetto.protos.Protolog.ProtoLogViewerConfig VIEWER_CONFIG = + perfetto.protos.Protolog.ProtoLogViewerConfig.newBuilder() + .addGroups( + perfetto.protos.Protolog.ProtoLogViewerConfig.Group.newBuilder() + .setId(1) + .setName(TestProtoLogGroup.TEST_GROUP.toString()) + .setTag(TestProtoLogGroup.TEST_GROUP.getTag()) + ).addMessages( + perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(123) + .setMessage("My Test Debug Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) + .setGroupId(1) + .setLocation("com/test/MyTestClass.java:123") + ).build(); + @BeforeClass public static void init() { ProtoLog.init(TestProtoLogGroup.values()); } @Before - public void setUp() { + public void setUp() throws ServiceNotFoundException { TestProtoLogGroup.TEST_GROUP.setLogToProto(mLogToProto); TestProtoLogGroup.TEST_GROUP.setLogToLogcat(mLogToLogcat); + + mProcessedProtoLogger = new ProcessedPerfettoProtoLogImpl( + MOCK_TEST_FILE_PATH, + () -> new AutoClosableProtoInputStream(VIEWER_CONFIG.toByteArray()), + () -> {}, + TestProtoLogGroup.values() + ); } @Test public void log_Processed_NoArgs() { - final var protoLog = ProtoLog.getSingleInstance(); final var perfMonitor = new PerfMonitor(); while (perfMonitor.keepRunning()) { - protoLog.log( + mProcessedProtoLogger.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123, 0, (Object[]) null); } @@ -90,11 +118,10 @@ public class ProtoLogPerfTest { @Test public void log_Processed_WithArgs() { - final var protoLog = ProtoLog.getSingleInstance(); final var perfMonitor = new PerfMonitor(); while (perfMonitor.keepRunning()) { - protoLog.log( + mProcessedProtoLogger.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123, 0b1110101001010100, new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true}); diff --git a/apex/blobstore/OWNERS b/apex/blobstore/OWNERS index 676cbc7eb2a3..f8208830d83e 100644 --- a/apex/blobstore/OWNERS +++ b/apex/blobstore/OWNERS @@ -1,4 +1,4 @@ -# Bug component: 25692 +# Bug component: 1628187 set noparent sudheersai@google.com diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig index 98e53ab97872..810be8fc4220 100644 --- a/apex/jobscheduler/service/aconfig/job.aconfig +++ b/apex/jobscheduler/service/aconfig/job.aconfig @@ -88,4 +88,11 @@ flag { namespace: "backstage_power" description: "Adjust quota default parameters" bug: "347058927" +} + +flag { + name: "enforce_quota_policy_to_top_started_jobs" + namespace: "backstage_power" + description: "Apply the quota policy to jobs started when the app was in TOP state" + bug: "374323858" }
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 885bad5e31c8..37e2fe2e46f1 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -619,7 +619,7 @@ public final class QuotaController extends StateController { } final int uid = jobStatus.getSourceUid(); - if (mTopAppCache.get(uid)) { + if (!Flags.enforceQuotaPolicyToTopStartedJobs() && mTopAppCache.get(uid)) { if (DEBUG) { Slog.d(TAG, jobStatus.toShortString() + " is top started job"); } @@ -656,7 +656,9 @@ public final class QuotaController extends StateController { timer.stopTrackingJob(jobStatus); } } - mTopStartedJobs.remove(jobStatus); + if (!Flags.enforceQuotaPolicyToTopStartedJobs()) { + mTopStartedJobs.remove(jobStatus); + } } @Override @@ -767,7 +769,7 @@ public final class QuotaController extends StateController { /** @return true if the job was started while the app was in the TOP state. */ private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) { - return mTopStartedJobs.contains(jobStatus); + return !Flags.enforceQuotaPolicyToTopStartedJobs() && mTopStartedJobs.contains(jobStatus); } /** Returns the maximum amount of time this job could run for. */ @@ -4379,11 +4381,13 @@ public final class QuotaController extends StateController { @Override public void dumpControllerStateLocked(final IndentingPrintWriter pw, final Predicate<JobStatus> predicate) { - pw.println("Flags: "); + pw.println("Aconfig Flags:"); pw.println(" " + Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS + ": " + Flags.adjustQuotaDefaultConstants()); pw.println(" " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS + ": " + Flags.enforceQuotaPolicyToFgsJobs()); + pw.println(" " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS + + ": " + Flags.enforceQuotaPolicyToTopStartedJobs()); pw.println(); pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis()); diff --git a/config/dirty-image-objects b/config/dirty-image-objects index f2e2b82cd82a..d4913d8f70d7 100644 --- a/config/dirty-image-objects +++ b/config/dirty-image-objects @@ -1,5 +1,5 @@ # -# Copyright (C) 2017 The Android Open Source Project +# 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. @@ -19,1710 +19,1435 @@ # 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. +# This particular file was generated by: +# https://android-build.corp.google.com/test_investigate/invocation/I55400010326683472/ # -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/content/ComponentCallbacks; 2 +Landroid/content/ComponentCallbacks2; 2 +Lcom/android/internal/app/ResolverActivity$ActionTitle;.HOME:Lcom/android/internal/app/ResolverActivity$ActionTitle;.name:Ljava/lang/String; 4 +Landroid/icu/text/MessageFormat;.typeList:[Ljava/lang/String;.1:Ljava/lang/String; 4 +Landroid/app/assist/AssistStructure$HtmlInfoNode; 4 +Landroid/app/ActivityManager$AppTask; 4 +Landroid/view/inputmethod/SurroundingText; 5 +Landroid/widget/ProgressBar$SavedState; 6 +Landroid/telephony/SignalStrength; 9 +Landroid/app/Notification$BigTextStyle; 10 +Landroid/app/AppOpsManager$OnOpNotedListener; 11 +Landroid/window/IRemoteTransition$Stub$Proxy; 11 +Lcom/android/internal/logging/UiEventLogger$UiEventEnum; 11 +Landroid/app/AppOpsManager$OnOpNotedInternalListener; 11 +Landroid/view/DisplayCutout; 11 +Lcom/android/internal/logging/MetricsLogger; 11 +Landroid/os/strictmode/LeakedClosableViolation; 11 +Lcom/android/internal/policy/AttributeCache; 11 +Lcom/android/internal/util/LatencyTracker$Action; 11 +Landroid/app/AppOpsManager$OnOpStartedListener; 11 +Landroid/app/Notification$DecoratedCustomViewStyle; 11 +Lcom/android/internal/R$styleable;.WindowAnimation:[I 11 +Landroid/app/Notification$CallStyle; 11 +Lcom/android/internal/statusbar/NotificationVisibility; 11 +Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal; 12 +Landroid/graphics/ColorSpace$Model;.RGB:Landroid/graphics/ColorSpace$Model; 13 +Landroid/app/WallpaperManager; 13 +Landroid/text/TextUtils$TruncateAt; 14 +Landroid/app/smartspace/uitemplatedata/BaseTemplateData; 15 +Landroid/app/smartspace/SmartspaceTarget; 15 +Lcom/android/internal/os/SomeArgs; 16 +Landroid/widget/inline/InlinePresentationSpec; 17 +Landroid/media/AudioSystem; 18 +Lcom/android/internal/os/CachedDeviceState$Readonly; 18 +Landroid/service/notification/Condition; 18 +Landroid/os/BatteryManagerInternal; 18 +Landroid/content/pm/PermissionGroupInfo; 18 +Landroid/app/job/JobInfo; 18 +Landroid/hardware/location/IActivityRecognitionHardwareClient; 18 +Landroid/accessibilityservice/AccessibilityServiceInfo; 18 +Landroid/app/Notification$DecoratedMediaCustomViewStyle; 18 +Landroid/hardware/location/NanoAppMessage; 18 +Landroid/net/Uri$PathPart;.NULL:Landroid/net/Uri$PathPart; 18 +Landroid/hardware/soundtrigger/SoundTrigger$StatusListener; 18 +Landroid/util/EventLog; 18 +Landroid/app/ActivityManager$RecentTaskInfo; 18 +Lcom/android/internal/widget/LockSettingsInternal; 18 +Landroid/window/DisplayAreaAppearedInfo; 18 +Landroid/accounts/AuthenticatorException; 18 +Landroid/os/ResultReceiver; 18 +Landroid/content/pm/UserPackage;.sCacheLock:Ljava/lang/Object; 18 +Lcom/android/internal/view/WindowManagerPolicyThread; 18 +Landroid/content/pm/LauncherActivityInfoInternal; 18 +Landroid/webkit/WebViewLibraryLoader$RelroFileCreator; 18 +Landroid/hardware/biometrics/BiometricSourceType; 18 +Landroid/net/metrics/ValidationProbeEvent; 18 +Landroid/view/RoundedCorners; 18 +Landroid/os/Process;.ZYGOTE_PROCESS:Landroid/os/ZygoteProcess; 18 +Landroid/app/ServiceStartArgs; 18 +Landroid/telecom/Logging/EventManager$EventListener; 18 +Landroid/app/SystemServiceRegistry; 18 +Landroid/permission/PermissionManagerInternal; 18 +Landroid/service/notification/StatusBarNotification; 18 +Lcom/android/internal/os/ProcessCpuTracker$FilterStats; 18 +Lcom/android/internal/util/ToBooleanFunction; 18 +Landroid/content/pm/RegisteredServicesCache$3; 18 +Landroid/os/ServiceManager$ServiceNotFoundException; 18 +Landroid/app/ActivityManagerInternal; 18 +Landroid/app/assist/AssistStructure; 18 +Landroid/hardware/camera2/CameraManager$CameraManagerGlobal; 18 +Landroid/app/servertransaction/PauseActivityItem; 18 +Landroid/hardware/soundtrigger/SoundTriggerModule$EventHandlerDelegate; 18 +Landroid/media/AudioAttributes; 18 +Landroid/service/dreams/DreamManagerInternal; 18 +Lcom/android/internal/listeners/ListenerExecutor$FailureCallback; 18 +Landroid/net/metrics/DhcpErrorEvent; 18 +Landroid/app/servertransaction/ConfigurationChangeItem; 18 +Landroid/hardware/sidekick/SidekickInternal; 18 +Landroid/appwidget/AppWidgetManagerInternal; 18 +Landroid/hardware/display/DisplayManagerInternal; 18 +Landroid/telecom/PhoneAccountHandle; 18 +Landroid/view/WindowManagerPolicyConstants; 18 +Landroid/net/wifi/nl80211/WifiNl80211Manager$ScanEventCallback; 18 +Lcom/android/internal/app/procstats/AssociationState;.sTmpSourceKey:Lcom/android/internal/app/procstats/AssociationState$SourceKey; 18 +Landroid/content/pm/RegisteredServicesCache$2; 18 +Landroid/content/pm/UserPackage; 18 +Landroid/app/time/TimeZoneCapabilities; 18 +Lcom/android/internal/util/function/LongObjPredicate; 18 +Landroid/app/servertransaction/NewIntentItem; 18 +Landroid/app/PropertyInvalidatedCache;.sCorkedInvalidates:Ljava/util/HashMap; 18 +Lcom/android/internal/os/StatsdHiddenApiUsageLogger;.sInstance:Lcom/android/internal/os/StatsdHiddenApiUsageLogger; 18 +Landroid/app/admin/DevicePolicyManagerInternal$OnCrossProfileWidgetProvidersChangeListener; 18 +Landroid/app/usage/AppStandbyInfo; 18 +Landroid/graphics/GraphicsStatsService; 18 +Lcom/android/internal/os/LongArrayMultiStateCounter; 18 +Landroid/graphics/Bitmap$CompressFormat; 18 +Landroid/media/audiopolicy/AudioVolumeGroup; 18 +Landroid/content/pm/CrossProfileAppsInternal; 18 +Landroid/os/PowerManagerInternal; 18 +Landroid/hardware/location/GeofenceHardwareImpl; 18 +Landroid/app/AppOpsManager$AttributedOpEntry; 18 +Landroid/attention/AttentionManagerInternal; 18 +Landroid/telecom/Log; 18 +Landroid/accounts/AccountManagerInternal; 18 +Landroid/content/pm/ShortcutServiceInternal$ShortcutChangeListener; 18 +Landroid/os/PatternMatcher;.sParsedPatternScratch:[I 18 +Landroid/app/AppOpsManager$NoteOpEvent; 18 +Lcom/android/internal/R$styleable;.Window:[I 18 +Landroid/content/pm/dex/ArtManagerInternal; 18 +Landroid/content/pm/PackageInstaller$SessionInfo; 18 +Landroid/app/servertransaction/StartActivityItem; 18 +Landroid/content/pm/PackageManager;.sCacheAutoCorker:Landroid/app/PropertyInvalidatedCache$AutoCorker; 18 +Landroid/graphics/Region;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 18 +Landroid/app/Notification$MediaStyle; 18 +Landroid/app/time/TimeZoneConfiguration; 18 +Lcom/android/internal/os/LongMultiStateCounter; 18 +Landroid/service/voice/VoiceInteractionManagerInternal; 18 +Landroid/view/Display$HdrCapabilities; 18 +Landroid/media/AudioSystem$AudioRecordingCallback; 18 +Landroid/app/servertransaction/DestroyActivityItem; 18 +Lcom/android/internal/os/RuntimeInit$ApplicationWtfHandler; 18 +Landroid/net/metrics/IpManagerEvent; 18 +Landroid/net/metrics/NetworkEvent; 18 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.7:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mLock:Ljava/lang/Object; 18 +Landroid/app/AppOpsManagerInternal; 18 +Landroid/content/res/ResourceTimer; 18 +Lcom/android/internal/infra/AbstractRemoteService$AsyncRequest; 18 +Lcom/android/internal/statusbar/NotificationVisibility$NotificationLocation; 18 +Landroid/app/servertransaction/ObjectPool;.sPoolSync:Ljava/lang/Object; 18 +Landroid/content/pm/PackageManager;.sCacheAutoCorker:Landroid/app/PropertyInvalidatedCache$AutoCorker;.mLock:Ljava/lang/Object; 18 +Landroid/app/admin/DevicePolicyManagerInternal; 18 +Landroid/webkit/WebViewZygote; 18 +Landroid/view/DisplayAddress$Physical; 18 +Landroid/accounts/AccountManagerInternal$OnAppPermissionChangeListener; 18 +Lcom/android/internal/os/BatteryStatsHistory$HistoryStepDetailsCalculator; 18 +Landroid/appwidget/AppWidgetProviderInfo; 18 +Landroid/provider/Settings; 18 +Lcom/android/server/AppWidgetBackupBridge; 18 +Landroid/content/pm/RegisteredServicesCacheListener; 18 +Landroid/window/IOnBackInvokedCallback$Stub$Proxy; 18 +Landroid/view/ViewDebug$ExportedProperty; 18 +Landroid/text/format/TimeFormatter; 18 +Landroid/content/ComponentName$WithComponentName; 18 +Landroid/hardware/location/ContextHubInfo; 18 +Landroid/app/servertransaction/ActivityResultItem; 18 +Landroid/hardware/display/DeviceProductInfo$ManufactureDate; 18 +Lcom/android/internal/util/function/DodecConsumer; 18 +Landroid/app/PropertyInvalidatedCache;.sCorks:Ljava/util/HashMap; 18 +Landroid/app/ApplicationExitInfo; 18 +Lcom/android/server/usage/AppStandbyInternal; 18 +Landroid/view/autofill/AutofillManagerInternal; 18 +Lcom/android/internal/infra/AbstractRemoteService$VultureCallback; 18 +Landroid/service/notification/NotificationListenerService$RankingMap; 18 +Landroid/service/notification/ConditionProviderService; 18 +Landroid/net/metrics/ApfProgramEvent; 18 +Lcom/android/internal/infra/AndroidFuture$1; 18 +Landroid/app/servertransaction/StopActivityItem; 18 +Landroid/app/PropertyInvalidatedCache$AutoCorker$1; 18 +Landroid/util/NtpTrustedTime; 18 +Landroid/content/pm/parsing/ApkLite; 18 +Lcom/android/server/LocalServices;.sLocalServiceObjects:Landroid/util/ArrayMap; 18 +Landroid/app/PropertyInvalidatedCache;.sCorkLock:Ljava/lang/Object; 18 +Landroid/app/PendingIntent$FinishedDispatcher; 18 +Landroid/os/ResultReceiver$MyResultReceiver; 18 +Landroid/os/ServiceSpecificException; 18 +Landroid/os/UEventObserver; 18 +Landroid/os/SharedMemory; 18 +Lcom/android/internal/util/function/DodecFunction; 18 +Landroid/content/pm/BaseParceledListSlice$1; 18 +Landroid/content/pm/FallbackCategoryProvider;.sFallbacks:Landroid/util/ArrayMap; 18 +Lcom/android/internal/content/om/OverlayConfig$PackageProvider; 18 +Landroid/util/ArrayMap;.sBaseCacheLock:Ljava/lang/Object; 18 +Landroid/debug/AdbManagerInternal; 18 +Landroid/view/WindowManagerPolicyConstants$PointerEventListener; 18 +Landroid/telephony/ServiceState; 18 +Lcom/android/internal/os/LooperStats; 18 +Landroid/content/pm/LauncherApps$ShortcutQuery$QueryFlags; 18 +Landroid/app/ActivityManager; 18 +Landroid/app/assist/ActivityId; 18 +Landroid/hardware/display/DeviceProductInfo; 18 +Lcom/android/internal/os/LongArrayMultiStateCounter;.sTmpArrayContainer:Ljava/util/concurrent/atomic/AtomicReference; 18 +Lcom/android/internal/os/LongArrayMultiStateCounter$LongArrayContainer; 18 +Landroid/service/notification/ZenPolicy; 18 +Landroid/content/pm/PackageManager$Property; 18 +Lcom/android/internal/content/om/OverlayConfig; 18 +Landroid/content/pm/ResolveInfo; 18 +Lcom/android/internal/os/KernelCpuBpfTracking; 18 +Landroid/content/pm/RegisteredServicesCache$1; 18 +Landroid/telecom/Logging/SessionManager$ISessionListener; 18 +Landroid/media/AudioPlaybackConfiguration$PlayerDeathMonitor; 18 +Landroid/service/autofill/FillContext; 18 +Landroid/graphics/Region;.sPool:Landroid/util/Pools$SynchronizedPool; 18 +Landroid/util/MemoryIntArray; 18 +Lcom/android/internal/config/appcloning/AppCloningDeviceConfigHelper; 18 +Landroid/os/storage/StorageManagerInternal; 18 +Landroid/media/AudioSystem$ErrorCallback; 18 +Landroid/service/notification/ZenModeConfig; 18 +Landroid/media/AudioPlaybackConfiguration; 18 +Landroid/content/pm/UserPackage;.sCache:Landroid/util/SparseArrayMap;.mData:Landroid/util/SparseArray; 18 +Landroid/app/AppOpsManager$SamplingStrategy; 18 +Landroid/app/servertransaction/ActivityConfigurationChangeItem; 18 +Landroid/hardware/location/GeofenceHardwareService; 18 +Landroid/os/RemoteCallback$1; 18 +Landroid/os/FileUtils$ProgressListener; 18 +Landroid/annotation/StringRes; 18 +Lcom/android/server/WidgetBackupProvider; 18 +Landroid/media/AudioManagerInternal$RingerModeDelegate; 18 +Landroid/hardware/biometrics/ComponentInfoInternal; 18 +Landroid/media/AudioManagerInternal; 18 +Landroid/media/AudioSystem$DynamicPolicyCallback; 18 +Landroid/os/DeadObjectException; 18 +Landroid/content/pm/ShortcutServiceInternal; 18 +Landroid/view/ViewDebug$FlagToString; 18 +Landroid/os/storage/StorageVolume; 18 +Landroid/window/TaskAppearedInfo; 18 +Lcom/android/internal/display/BrightnessSynchronizer; 18 +Lcom/android/internal/infra/ServiceConnector$Impl$CompletionAwareJob; 18 +Landroid/os/strictmode/UnbufferedIoViolation; 19 +Landroid/app/usage/UsageStats; 20 +Landroid/app/usage/CacheQuotaHint; 21 +Landroid/service/watchdog/ExplicitHealthCheckService$PackageConfig; 21 +Landroid/os/BaseBundle; 22 +Landroid/os/Parcel$ReadWriteHelper; 23 +Landroid/util/SparseArray; 24 +Landroid/app/Instrumentation; 25 +Landroid/content/pm/PathPermission; 26 +[Landroid/content/pm/ConfigurationInfo; 26 +Landroid/content/pm/ActivityInfo; 26 +Landroid/content/pm/Attribution; 26 +Landroid/content/pm/ConfigurationInfo; 26 +Landroid/content/pm/ActivityInfo$WindowLayout; 26 +Landroid/os/PatternMatcher; 26 +Landroid/content/pm/ServiceInfo; 26 +[Landroid/content/pm/FeatureInfo; 26 +[Landroid/content/pm/FeatureGroupInfo; 26 +Landroid/content/pm/SigningInfo; 26 +[Landroid/content/pm/InstrumentationInfo; 26 +[Landroid/content/pm/ServiceInfo; 26 +[Landroid/os/PatternMatcher; 26 +Landroid/content/pm/ComponentInfo; 26 +[Landroid/content/pm/PermissionInfo; 26 +[Landroid/content/pm/ActivityInfo; 26 +[Landroid/content/pm/Attribution; 26 +Landroid/content/pm/SharedLibraryInfo; 26 +Landroid/content/pm/FeatureGroupInfo; 26 +Landroid/content/pm/InstrumentationInfo; 26 +[Landroid/content/pm/Signature; 26 +[Landroid/content/pm/ProviderInfo; 26 +[Landroid/content/pm/PathPermission; 26 +Landroid/content/pm/Signature; 26 +Landroid/graphics/drawable/NinePatchDrawable; 28 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_APERTURES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 29 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_FOCAL_LENGTHS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 30 +Landroid/hardware/camera2/params/StreamConfigurationDuration; 31 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/params/StreamConfiguration; 31 +Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_CAPABILITIES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/params/HighSpeedVideoConfiguration; 31 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 31 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_FACING:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 32 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 33 +Landroid/hardware/camera2/marshal/MarshalRegistry;.sMarshalerMap:Ljava/util/HashMap; 33 +Landroid/hardware/devicestate/DeviceStateManagerGlobal; 34 +Lgov/nist/javax/sip/header/AuthenticationHeader;.SIGNATURE:Ljava/lang/String; 40 +Landroid/app/slice/Slice;.SUBTYPE_SOURCE:Ljava/lang/String; 41 +Ljavax/sip/message/Request;.INFO:Ljava/lang/String; 41 +Lgov/nist/javax/sip/header/extensions/ReferencesHeader;.SERVICE:Ljava/lang/String; 42 +Landroid/icu/impl/locale/LocaleValidityChecker$SpecialCase;.normal:Landroid/icu/impl/locale/LocaleValidityChecker$SpecialCase;.name:Ljava/lang/String; 42 +Lgov/nist/javax/sip/address/NetObject;.PHONE:Ljava/lang/String; 42 +Landroid/icu/impl/ValidIdentifiers$Datatype;.language:Landroid/icu/impl/ValidIdentifiers$Datatype;.name:Ljava/lang/String; 42 +Landroid/icu/text/MessageFormat;.dateModifierList:[Ljava/lang/String;.3:Ljava/lang/String; 42 +Landroid/view/translation/UiTranslationManager;.EXTRA_PACKAGE_NAME:Ljava/lang/String; 42 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.140:Ljava/lang/String; 43 +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; 44 +Lcom/android/i18n/timezone/TimeZoneFinder;.COUNTRY_ELEMENT:Ljava/lang/String; 45 +Landroid/app/usage/UsageEvents$Event;.DEVICE_EVENT_PACKAGE_NAME:Ljava/lang/String; 46 +Landroid/icu/text/MessageFormat;.rootLocale:Ljava/util/Locale;.baseLocale:Lsun/util/locale/BaseLocale;.language:Ljava/lang/String; 47 +Landroid/graphics/Bitmap; 68 +Landroid/view/inputmethod/IInputMethodManagerGlobalInvoker; 69 +Landroid/view/SurfaceControlRegistry; 70 +Lcom/android/internal/inputmethod/ImeTracing; 70 +Lcom/android/internal/policy/DecorView; 71 +Landroid/view/ViewTreeObserver; 71 +Landroid/view/accessibility/AccessibilityNodeIdManager; 71 +Landroid/view/ViewRootImpl; 71 +Landroid/widget/FrameLayout; 71 +Landroid/view/ViewStub; 71 +Landroid/window/SurfaceSyncGroup; 72 +Landroid/view/Choreographer; 73 +Landroid/os/SystemProperties;.sChangeCallbacks:Ljava/util/ArrayList; 74 +Landroid/app/ActivityClient;.sInstance:Landroid/util/Singleton; 75 +Landroid/app/ActivityClient;.INTERFACE_SINGLETON:Landroid/app/ActivityClient$ActivityClientControllerSingleton; 75 +Landroid/view/autofill/AutofillId; 76 +Landroid/os/StrictMode$InstanceTracker;.sInstanceCounts:Ljava/util/HashMap; 77 +Landroid/widget/LinearLayout; 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/view/WindowManagerImpl; 80 +Landroid/view/accessibility/AccessibilityManager; 81 +Landroid/app/ActivityTaskManager;.IActivityTaskManagerSingleton:Landroid/util/Singleton; 82 +Landroid/os/Handler; 83 +Landroid/transition/ChangeImageTransform; 109 +Landroid/transition/ChangeClipBounds; 109 +Landroid/transition/ChangeTransform; 109 +Landroid/graphics/drawable/ColorDrawable; 110 +Landroid/text/TextUtils; 111 +Landroid/graphics/TemporaryBuffer; 112 +Landroid/text/TextLine;.sCached:[Landroid/text/TextLine; 113 +Landroid/text/Layout;.sTempRect:Landroid/graphics/Rect; 114 +Landroid/widget/ImageView; 115 +Landroid/content/res/ColorStateList;.sCache:Landroid/util/SparseArray; 116 +Landroid/graphics/Bitmap;.sAllBitmaps:Ljava/util/WeakHashMap; 117 +Landroid/icu/impl/locale/BaseLocale;.CACHE:Landroid/icu/impl/locale/BaseLocale$Cache;._map:Ljava/util/concurrent/ConcurrentHashMap; 118 +Landroid/database/sqlite/SQLiteGlobal; 125 +Landroid/database/sqlite/SQLiteDebug$NoPreloadHolder; 125 +Landroid/database/sqlite/SQLiteCompatibilityWalFlags; 125 +Landroid/database/CursorWindow; 126 +Landroid/content/SharedPreferences; 127 +Landroid/app/AppOpsManager; 132 +Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap;.mHashes:[I 133 +Landroid/media/MediaFrameworkPlatformInitializer; 133 +Lcom/android/internal/os/RuntimeInit; 133 +Landroid/os/Message; 133 +Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map; 133 +Landroid/os/ServiceManager; 133 +Landroid/se/omapi/SeFrameworkInitializer; 133 +Landroid/ddm/DdmHandleAppName; 133 +Landroid/security/net/config/ApplicationConfig; 133 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.7:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 133 +Landroid/os/Environment; 133 +Landroid/os/Looper; 133 +Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map;.table:[Ljava/util/WeakHashMap$Entry; 133 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.7:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 133 +Landroid/os/StrictMode; 133 +Landroid/provider/DeviceConfigInitializer; 133 +Landroid/graphics/Typeface; 133 +Landroid/app/ActivityThread; 133 +Landroid/view/View; 133 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap; 133 +Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object; 133 +Landroid/telephony/TelephonyFrameworkInitializer; 133 +Landroid/util/ArraySet; 133 +Landroid/os/Binder; 133 +Landroid/os/LocaleList; 133 +Landroid/hardware/display/DisplayManagerGlobal; 133 +Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap; 133 +Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder; 133 +Landroid/os/DdmSyncState; 133 +Landroid/os/GraphicsEnvironment;.sInstance:Landroid/os/GraphicsEnvironment; 134 +Landroid/os/Parcel;.mCreators:Ljava/util/HashMap; 134 +Landroid/os/Parcel; 134 +Landroid/app/ApplicationLoaders;.gApplicationLoaders:Landroid/app/ApplicationLoaders;.mLoaders:Landroid/util/ArrayMap; 134 +Landroid/os/Process; 134 +Landroid/os/Parcel;.sPairedCreators:Ljava/util/HashMap; 134 +Landroid/graphics/Compatibility; 135 +Landroid/app/DexLoadReporter;.INSTANCE:Landroid/app/DexLoadReporter;.mDataDirs:Ljava/util/Set;.map:Ljava/util/HashMap; 135 +Landroid/renderscript/RenderScriptCacheDir; 135 +Landroid/graphics/Canvas; 135 +Landroid/provider/FontsContract; 135 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.4:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 136 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.4:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 136 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.9:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 139 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.9:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 140 +Lcom/android/internal/os/BinderInternal; 141 +Landroid/app/servertransaction/ClientTransactionListenerController; 142 +Landroid/app/QueuedWork; 143 +Landroid/view/WindowManagerGlobal; 144 +Landroid/view/WindowManager; 145 +Landroid/view/inputmethod/InputMethodManager; 146 +Landroid/telephony/TelephonyManager; 147 +Landroid/content/Context; 151 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.4:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mSkips:[J 152 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.9:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mSkips:[J 153 +Landroid/app/NotificationManager; 154 +Landroid/content/pm/VersionedPackage; 155 +Landroid/content/ContentResolver; 156 +Landroid/content/SharedPreferences$OnSharedPreferenceChangeListener; 160 +Landroid/app/UiModeManager; 162 +Landroid/webkit/WebViewFactory; 163 +Landroid/webkit/WebViewFactory;.sTimestamps:Landroid/webkit/WebViewFactory$StartupTimestamps; 164 +Landroid/webkit/WebViewFactoryProvider; 165 +Landroid/webkit/WebViewProvider$ViewDelegate; 166 +Landroid/webkit/WebViewProvider; 166 +Landroid/webkit/WebViewProvider$ScrollDelegate; 166 +Landroid/webkit/CookieSyncManager; 167 +Landroid/media/MediaCodecList; 168 +Landroid/media/MediaCodecInfo$CodecCapabilities$FeatureList; 168 +Landroid/media/MediaCodec; 169 +Landroid/view/KeyEvent; 170 +Landroid/webkit/HttpAuthHandler; 171 +Landroid/webkit/WebResourceResponse; 171 +Landroid/webkit/WebViewClient; 171 +Landroid/webkit/WebResourceError; 171 +Landroid/webkit/WebView; 171 +Landroid/view/InputEvent; 171 +Landroid/webkit/RenderProcessGoneDetail; 171 +Landroid/webkit/WebResourceRequest; 172 +Landroid/view/PointerIcon;.SYSTEM_ICONS:Landroid/util/SparseArray; 173 +Landroid/content/ClipboardManager$OnPrimaryClipChangedListener; 174 +Landroid/hardware/input/InputManager$InputDeviceListener; 175 +Landroid/hardware/input/InputManagerGlobal; 176 +Landroid/window/WindowTokenClientController; 177 +Landroid/os/PowerManager$OnThermalStatusChangedListener; 178 +Landroid/webkit/WebViewFactoryProvider$Statics; 179 +Landroid/webkit/DownloadListener; 180 +Landroid/webkit/ConsoleMessage; 181 +Landroid/webkit/GeolocationPermissions$Callback; 181 +Landroid/webkit/PermissionRequest; 181 +Landroid/webkit/WebChromeClient$CustomViewCallback; 181 +Landroid/webkit/WebChromeClient; 181 +Landroid/webkit/ValueCallback; 182 +Landroid/view/View$OnDragListener; 183 +Landroid/view/autofill/Helper; 184 +Landroid/icu/impl/ICUResourceBundleReader;.CACHE:Landroid/icu/impl/ICUResourceBundleReader$ReaderCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 185 +Landroid/icu/impl/ZoneMeta;.CANONICAL_ID_CACHE:Landroid/icu/impl/ICUCache; 186 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.10:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 187 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.10:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 188 +Landroid/graphics/fonts/Font$NoImagePreloadHolder; 189 +Landroid/icu/text/DecimalFormatSymbols;.cachedLocaleData:Landroid/icu/impl/CacheBase;.map:Ljava/util/concurrent/ConcurrentHashMap; 198 +Landroid/icu/impl/CurrencyData;.provider:Landroid/icu/impl/CurrencyData$CurrencyDisplayInfoProvider; 199 +Landroid/icu/text/DateFormatSymbols;.DFSCACHE:Landroid/icu/impl/CacheBase;.map:Ljava/util/concurrent/ConcurrentHashMap; 204 +Landroid/icu/util/ULocale; 205 +Landroid/graphics/Paint;.sMinikinLocaleListIdCache:Ljava/util/HashMap; 206 +Landroid/os/Parcelable$Creator; 207 +Landroid/graphics/drawable/Drawable; 215 +Landroid/graphics/Bitmap$Config;.RGB_565:Landroid/graphics/Bitmap$Config; 218 +Landroid/graphics/Bitmap$Config;.RGBA_F16:Landroid/graphics/Bitmap$Config; 218 +Landroid/renderscript/Allocation;.mBitmapOptions:Landroid/graphics/BitmapFactory$Options;.inPreferredConfig:Landroid/graphics/Bitmap$Config; 218 +Landroid/graphics/Bitmap$Config;.HARDWARE:Landroid/graphics/Bitmap$Config; 218 +Landroid/graphics/Bitmap$Config;.RGBA_1010102:Landroid/graphics/Bitmap$Config; 218 +Landroid/graphics/Bitmap$Config;.ALPHA_8:Landroid/graphics/Bitmap$Config; 218 +Landroid/graphics/Bitmap$Config;.ARGB_4444:Landroid/graphics/Bitmap$Config; 218 +Landroid/os/ParcelFileDescriptor; 219 +Landroid/content/ComponentName; 224 +Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle; 225 +Landroid/database/sqlite/SQLiteDatabase$CursorFactory; 226 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.7:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mSkips:[J 227 +Landroid/util/ArrayMap; 229 +Landroid/media/AudioManager; 230 +Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map;.queue:Ljava/lang/ref/ReferenceQueue; 231 +Lcom/android/internal/util/ArrayUtils;.sCache:[Ljava/lang/Object; 232 +Landroid/text/SpanWatcher; 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/LeadingMarginSpan; 235 +Landroid/text/style/TabStopSpan; 236 +Landroid/text/style/LineBreakConfigSpan; 237 +Landroid/text/style/MetricAffectingSpan; 238 +Landroid/text/style/LineHeightSpan; 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 +Landroid/text/DynamicLayout$ChangeWatcher; 241 +Landroid/text/DynamicLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool; 242 +Landroid/text/DynamicLayout; 242 +Landroid/text/DynamicLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 242 +Landroid/text/style/WrapTogetherSpan; 243 +Landroid/widget/TextView$ChangeWatcher; 244 +Landroid/text/Selection$MemoryTextWatcher; 245 +Landroid/text/SpannableStringBuilder;.sCachedIntBuffer:[[I 246 +Landroid/text/style/SuggestionSpan; 247 +Landroid/text/style/ReplacementSpan; 248 +Landroid/text/TextUtils$TruncateAt;.MARQUEE:Landroid/text/TextUtils$TruncateAt; 249 +Landroid/text/style/SpellCheckSpan; 250 +Landroid/text/method/ArrowKeyMovementMethod; 251 +Landroid/text/method/TextKeyListener;.sInstance:[Landroid/text/method/TextKeyListener; 251 +Landroid/view/textclassifier/TextClassificationConstants; 252 +Landroid/text/Selection;.SELECTION_START:Ljava/lang/Object; 253 +Landroid/text/Selection;.SELECTION_END:Ljava/lang/Object; 253 +Landroid/text/Selection;.SELECTION_MEMORY:Ljava/lang/Object; 253 +Landroid/widget/EditText; 254 +Landroid/view/autofill/AutofillValue; 255 +Landroid/view/ViewGroup$ChildListForAutoFillOrContentCapture;.sPool:Landroid/util/Pools$SimplePool; 256 +Landroid/view/ViewGroup$ChildListForAutoFillOrContentCapture;.sPool:Landroid/util/Pools$SimplePool;.mPool:[Ljava/lang/Object; 256 +Landroid/view/ViewGroup; 257 +Landroid/widget/TextView; 258 +Landroid/animation/AnimatorInflater;.sTmpTypedValue:Landroid/util/TypedValue; 259 +Landroid/graphics/drawable/GradientDrawable; 260 +Landroid/text/method/SingleLineTransformationMethod; 261 +Landroid/text/StaticLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 262 +Landroid/text/StaticLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool; 262 +Landroid/os/HandlerThread; 263 +Lcom/android/internal/os/ZygoteInit; 263 +Landroid/database/DatabaseUtils; 263 +Landroid/annotation/CurrentTimeMillisLong; 264 +Landroid/app/NotificationChannel; 265 +Landroid/os/AsyncTask; 267 +Landroid/graphics/Bitmap;.sAllBitmaps:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry; 285 +Lcom/android/internal/telephony/MccTable;.FALLBACKS:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.6:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 288 +Landroid/view/Window$OnFrameMetricsAvailableListener; 289 +Landroid/graphics/drawable/BitmapDrawable; 290 +Landroid/graphics/drawable/RippleDrawable; 291 +Landroid/animation/PropertyValuesHolder$FloatPropertyValuesHolder;.sJNISetterPropertyMap:Ljava/util/HashMap; 292 +Landroid/animation/PropertyValuesHolder;.sGetterPropertyMap:Ljava/util/HashMap; 293 +Landroid/graphics/drawable/LayerDrawable; 294 +Landroid/media/audiopolicy/AudioProductStrategy;.sLock:Ljava/lang/Object; 304 +Landroid/graphics/drawable/RotateDrawable; 304 +Landroid/opengl/EGLConfig; 307 +Landroid/icu/impl/ValidIdentifiers$Datasubtype;.unknown:Landroid/icu/impl/ValidIdentifiers$Datasubtype;.name:Ljava/lang/String; 308 +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; 308 +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; 309 +Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.0:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 310 +Landroid/telephony/ims/ImsService;.CAPABILITIES_LOG_MAP:Ljava/util/Map;.table:[Ljava/lang/Object;.2:Ljava/lang/Long; 311 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.569:Ljava/lang/Long; 311 +Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.6:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 312 +Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.1:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 313 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.11:Ljava/lang/Boolean; 314 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1:Ljava/lang/Boolean; 315 +Landroid/os/SystemProperties; 325 +Landroid/app/job/JobParameters; 328 +Landroid/view/Window$DecorCallback; 329 +Landroid/view/MenuItem$OnActionExpandListener; 329 +Landroid/view/inputmethod/EditorInfo; 329 +Lcom/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry; 329 +Landroid/app/ActivityManager$OnUidImportanceListener; 331 +Landroid/os/strictmode/DiskReadViolation; 332 +Landroid/os/strictmode/CustomViolation; 333 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.4:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mLock:Ljava/lang/Object; 334 +Landroid/location/Location; 337 +Landroid/database/sqlite/SQLiteConstraintException; 337 +Lcom/android/internal/listeners/ListenerTransport; 338 +Landroid/content/IntentFilter; 339 +Landroid/hardware/location/ContextHubTransaction$OnCompleteListener; 340 +Landroid/app/PendingIntent$OnFinished; 340 +Landroid/os/WorkSource; 340 +Landroid/content/pm/PackageManager$OnPermissionsChangedListener; 341 +Landroid/annotation/IdRes; 342 +Landroid/app/AppOpsManager$Mode; 343 +Landroid/view/accessibility/AccessibilityManager$AccessibilityServicesStateChangeListener; 344 +Landroid/telephony/DataSpecificRegistrationInfo; 345 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle; 346 +Landroid/telephony/TelephonyRegistryManager;.sCarrierPrivilegeCallbacks:Ljava/util/WeakHashMap; 347 +Landroid/telephony/NetworkRegistrationInfo; 347 +Lcom/android/internal/telephony/TelephonyPermissions;.sReportedDeviceIDPackages:Ljava/util/Map; 347 +Landroid/telephony/AnomalyReporter; 347 +Landroid/content/ContentProvider$Transport; 347 +Landroid/database/CursorToBulkCursorAdaptor; 347 +Landroid/telephony/TelephonyRegistryManager;.sCarrierPrivilegeCallbacks:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry; 347 +Landroid/telephony/VoiceSpecificRegistrationInfo; 347 +Landroid/net/MatchAllNetworkSpecifier; 347 +Landroid/app/PropertyInvalidatedCache$NoPreloadHolder; 348 +Landroid/app/PropertyInvalidatedCache;.sInvalidates:Ljava/util/HashMap; 348 +Landroid/app/PropertyInvalidatedCache;.sDisabledKeys:Ljava/util/HashSet;.map:Ljava/util/HashMap; 349 +Landroid/media/session/MediaSessionManager$SessionsChangedWrapper$1; 350 +Landroid/media/session/MediaSessionManager$OnMediaKeyEventSessionChangedListener; 350 +Landroid/media/AudioManager$2; 350 +Landroid/app/PendingIntent$CancelListener; 350 +Landroid/app/ActivityManager$MyUidObserver; 350 +Landroid/app/compat/CompatChanges;.QUERY_CACHE:Landroid/app/compat/ChangeIdStateCache;.mCache:Ljava/util/LinkedHashMap; 351 +Landroid/app/compat/CompatChanges;.QUERY_CACHE:Landroid/app/compat/ChangeIdStateCache; 351 +Landroid/app/AlarmManager; 352 +Landroid/os/UserManager; 353 +Landroid/text/MeasuredParagraph;.sPool:Landroid/util/Pools$SynchronizedPool; 355 +Landroid/text/MeasuredParagraph;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 355 +Landroid/database/sqlite/SQLiteTransactionListener; 357 +Landroid/text/format/DateFormat; 358 +Landroid/icu/util/Calendar;.PATTERN_CACHE:Landroid/icu/impl/ICUCache; 359 +Landroid/icu/impl/DateNumberFormat;.CACHE:Landroid/icu/impl/SimpleCache; 360 +Landroid/app/smartspace/SmartspaceSession$OnTargetsAvailableListener; 361 +Lcom/android/internal/util/PerfettoTrigger;.sLastInvocationPerTrigger:Landroid/util/SparseLongArray; 361 +Lcom/android/internal/util/PerfettoTrigger;.sLastInvocationPerTrigger:Landroid/util/SparseLongArray;.mValues:[J 361 +Landroid/window/WindowOrganizer;.IWindowOrganizerControllerSingleton:Landroid/util/Singleton; 361 +Landroid/window/WindowContainerTransaction$Change; 361 +Lcom/android/internal/util/PerfettoTrigger;.sLastInvocationPerTrigger:Landroid/util/SparseLongArray;.mKeys:[I 361 +Landroid/view/ViewTreeObserver$OnWindowVisibilityChangeListener; 361 +Landroid/view/CrossWindowBlurListeners; 362 +Landroid/widget/Toast; 363 +Landroid/view/ViewStub$OnInflateListener; 364 +Landroid/text/Spanned; 365 +Lcom/android/internal/policy/PhoneLayoutInflater; 365 +Landroid/content/MutableContextWrapper; 365 +Landroid/renderscript/RenderScript; 365 +Landroid/content/pm/IPackageManager$Stub$Proxy; 367 +Landroid/media/MediaPlayer$EventHandler; 368 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.220:Ljava/lang/String; 368 +Landroid/graphics/drawable/GradientDrawable$Orientation;.BOTTOM_TOP:Landroid/graphics/drawable/GradientDrawable$Orientation; 368 +Landroid/graphics/drawable/GradientDrawable$Orientation;.RIGHT_LEFT:Landroid/graphics/drawable/GradientDrawable$Orientation; 368 +Landroid/content/res/ResourcesImpl; 368 +Landroid/os/ResultReceiver$MyRunnable; 368 +Landroid/graphics/drawable/GradientDrawable$Orientation;.TL_BR:Landroid/graphics/drawable/GradientDrawable$Orientation; 368 +Landroid/view/animation/Animation$3; 368 +Landroid/view/ViewRootImpl$7; 368 +Landroid/view/animation/Animation$1; 368 +Landroid/graphics/drawable/GradientDrawable$Orientation;.BR_TL:Landroid/graphics/drawable/GradientDrawable$Orientation; 368 +Landroid/graphics/drawable/GradientDrawable$Orientation;.TOP_BOTTOM:Landroid/graphics/drawable/GradientDrawable$Orientation; 368 +Landroid/os/PowerManager$3$$ExternalSyntheticLambda0; 368 +Landroid/graphics/drawable/GradientDrawable$Orientation;.BL_TR:Landroid/graphics/drawable/GradientDrawable$Orientation; 368 +Landroid/graphics/drawable/GradientDrawable$Orientation;.LEFT_RIGHT:Landroid/graphics/drawable/GradientDrawable$Orientation; 368 +Landroid/graphics/drawable/GradientDrawable$Orientation;.TR_BL:Landroid/graphics/drawable/GradientDrawable$Orientation; 368 +Lcom/android/internal/policy/PhoneWindow$1; 368 +Landroid/hardware/SensorManager; 368 +Landroid/widget/SeekBar; 369 +Landroid/media/MediaRouter2Manager; 370 +Landroid/app/trust/TrustManager$TrustListener; 370 +Landroid/permission/PermissionManager;.INDICATOR_EXEMPTED_PACKAGES:[Ljava/lang/String; 370 +Landroid/media/session/MediaSessionManager$SessionsChangedWrapper$1$$ExternalSyntheticLambda0; 370 +Landroid/view/ViewOverlay$OverlayViewGroup; 370 +Landroid/hardware/display/NightDisplayListener$Callback; 370 +Lcom/android/internal/widget/NotificationOptimizedLinearLayout; 370 +Landroid/hardware/biometrics/BiometricSourceType;.IRIS:Landroid/hardware/biometrics/BiometricSourceType; 370 +Landroid/view/NotificationTopLineView; 370 +Landroid/permission/PermissionManager; 370 +Landroid/text/TextShaper$GlyphsConsumer; 370 +Lcom/android/internal/widget/RemeasuringLinearLayout; 370 +Landroid/os/HandlerExecutor; 370 +Landroid/hardware/biometrics/BiometricSourceType;.FACE:Landroid/hardware/biometrics/BiometricSourceType; 370 +Landroid/animation/ValueAnimator$DurationScaleChangeListener; 370 +Landroid/widget/RemoteViews;.sLookupKey:Landroid/widget/RemoteViews$MethodKey; 370 +Lcom/android/internal/logging/UiEventLogger; 370 +Lcom/android/internal/view/menu/ActionMenuItemView; 370 +Landroid/hardware/biometrics/BiometricSourceType;.FINGERPRINT:Landroid/hardware/biometrics/BiometricSourceType; 370 +Landroid/transition/TransitionManager;.sPendingTransitions:Ljava/util/ArrayList; 370 +Landroid/graphics/drawable/DrawableInflater;.CONSTRUCTOR_MAP:Ljava/util/HashMap; 370 +Lcom/android/internal/widget/ImageFloatingTextView; 370 +Lcom/android/internal/widget/CachingIconView; 370 +Lcom/android/internal/widget/MessagingLayout; 370 +Landroid/widget/DateTimeView$ReceiverInfo$1; 370 +Landroid/view/animation/AnimationSet; 370 +Landroid/hardware/face/FaceManager$FaceDetectionCallback; 370 +Landroid/view/SurfaceControl; 370 +Lcom/android/internal/widget/NotificationExpandButton; 370 +Landroid/widget/ViewSwitcher;.dexCache:Ljava/lang/Object; 370 +Lcom/android/internal/colorextraction/ColorExtractor$OnColorsChangedListener; 370 +Landroid/view/RemotableViewMethod; 370 +Landroid/view/View;.SCALE_Y:Landroid/util/Property; 370 +Landroid/view/View;.TRANSLATION_Y:Landroid/util/Property; 370 +Landroid/telephony/satellite/SatelliteManager;.sSatelliteSupportedStateCallbackMap:Ljava/util/concurrent/ConcurrentHashMap; 370 +Landroid/view/NotificationHeaderView; 370 +Lcom/android/internal/widget/ImageResolver; 370 +Landroid/hardware/display/DisplayManagerGlobal$DisplayListenerDelegate$$ExternalSyntheticLambda0; 370 +Lcom/android/internal/widget/ConversationLayout; 370 +Lcom/android/internal/util/ContrastColorUtil; 370 +Landroid/text/format/DateUtils; 370 +Landroid/widget/RemoteViews;.sMethods:Landroid/util/ArrayMap; 370 +Landroid/widget/DateTimeView; 370 +Lcom/android/internal/widget/NotificationActionListLayout; 370 +Landroid/view/View;.SCALE_X:Landroid/util/Property; 370 +Landroid/widget/GridLayout;.UNDEFINED_ALIGNMENT:Landroid/widget/GridLayout$Alignment; 372 +Landroid/database/CursorIndexOutOfBoundsException; 374 +Lcom/android/internal/policy/DecorView$2; 375 +Landroid/widget/Spinner; 376 +Landroid/security/keystore2/AndroidKeyStoreRSAPrivateKey; 376 +Landroid/security/keystore/KeyInfo; 377 +Landroid/security/keystore2/AndroidKeyStoreECPrivateKey; 378 +Landroid/text/method/TextKeyListener;.ACTIVE:Ljava/lang/Object; 379 +Landroid/text/method/PasswordTransformationMethod; 380 +Landroid/speech/tts/TextToSpeech$Connection$SetupConnectionAsyncTask; 382 +Landroid/speech/tts/TextToSpeech$OnInitListener; 383 +Lcom/android/internal/policy/PhoneWindow; 384 +Lcom/android/internal/policy/PhoneWindow$PanelFeatureState$SavedState; 385 +Landroid/content/res/Configuration; 386 +Landroid/window/WindowContext; 386 +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; 386 +Landroid/app/Dialog$$ExternalSyntheticLambda2; 386 +Landroid/app/prediction/AppTargetEvent; 386 +Landroid/app/prediction/AppTarget; 388 +Landroid/content/res/Resources$NotFoundException; 389 +Landroid/icu/text/Collator; 390 +Landroid/widget/TextView$SavedState; 391 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.4:Ljava/util/WeakHashMap$Entry; 393 +Lcom/android/internal/logging/AndroidHandler; 395 +Landroid/view/VelocityTracker;.sPool:Landroid/util/Pools$SynchronizedPool; 397 +Landroid/os/StrictMode$OnThreadViolationListener; 397 +Landroid/view/VelocityTracker;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 397 +Landroid/content/Context;.ACCOUNT_SERVICE:Ljava/lang/String; 397 +Landroid/service/trust/TrustAgentService;.EXTRA_TOKEN:Ljava/lang/String; 397 +Ljavax/sip/header/AcceptEncodingHeader;.NAME:Ljava/lang/String; 397 +Landroid/widget/RadioGroup$OnCheckedChangeListener; 398 +Lcom/android/internal/widget/DialogTitle; 399 +Lcom/android/internal/widget/ButtonBarLayout; 399 +Lcom/android/internal/widget/AlertDialogLayout; 399 +Landroid/icu/text/DecimalFormatSymbols;.DEF_DIGIT_STRINGS_ARRAY:[Ljava/lang/String;.1:Ljava/lang/String; 401 +Landroid/widget/Editor$TextRenderNode; 402 +Landroid/view/inputmethod/DeleteGesture; 403 +Landroid/text/method/MetaKeyKeyListener;.ALT:Ljava/lang/Object; 403 +Landroid/view/inputmethod/SelectRangeGesture; 403 +Landroid/text/method/MetaKeyKeyListener;.CAP:Ljava/lang/Object; 403 +Landroid/view/inputmethod/DeleteRangeGesture; 403 +Landroid/text/method/MetaKeyKeyListener;.SYM:Ljava/lang/Object; 403 +Landroid/text/method/MetaKeyKeyListener;.SELECTING:Ljava/lang/Object; 403 +Landroid/view/inputmethod/SelectGesture; 403 +Landroid/widget/TextView;.TEMP_POSITION:[F 404 +Landroid/view/inputmethod/BaseInputConnection;.COMPOSING:Ljava/lang/Object; 405 +Lcom/android/internal/infra/AndroidFuture; 406 +Landroid/accounts/Account;.sAccessedAccounts:Ljava/util/Set; 407 +Landroid/os/Message;.sPoolSync:Ljava/lang/Object; 408 +Landroid/database/sqlite/SQLiteCantOpenDatabaseException; 409 +Landroid/accounts/Account; 409 +Landroid/os/VibrationEffect; 411 +Landroid/content/ServiceConnection; 411 +Landroid/app/ActivityManager$MemoryInfo; 411 +Landroid/util/DisplayMetrics; 411 +Landroid/view/Display; 411 +Landroid/telephony/TelephonyCallback$DataConnectionStateListener; 412 +Landroid/hardware/display/IDisplayManager; 414 +Lcom/android/icu/util/regex/PatternNative; 414 +Landroid/view/WindowInsets; 414 +Landroid/app/ActivityTaskManager$2; 414 +Landroid/view/View$AttachInfo; 414 +Landroid/media/AudioManager$ServiceEventHandlerDelegate$1; 414 +Landroid/view/ViewRootImpl$6; 414 +Landroid/webkit/WebViewDelegate; 414 +Landroid/os/IInterface; 415 +Landroid/content/pm/IPackageManager; 417 +Landroid/app/IActivityManager; 418 +Landroid/text/style/ImageSpan; 419 +Landroid/widget/RelativeLayout; 422 +Landroid/graphics/drawable/StateListDrawable; 423 +Landroid/view/TextureView$SurfaceTextureListener; 427 +Landroid/graphics/SurfaceTexture; 428 +Landroid/media/audiopolicy/AudioProductStrategy; 429 +Landroid/media/PlayerBase; 430 +Landroid/os/FileUtils; 431 +Landroid/media/MediaDrm$OnEventListener; 435 +Landroid/graphics/drawable/TransitionDrawable; 436 +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; 437 +Lcom/android/ims/rcs/uce/presence/pidfparser/omapres/Version;.ELEMENT_NAME:Ljava/lang/String; 442 +Landroid/view/Window$Callback; 442 +Landroid/provider/SyncStateContract$Columns;.DATA:Ljava/lang/String; 442 +Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap;.mHashes:[I 444 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedString; 444 +Landroid/aconfig/nano/Aconfig$tracepoint; 444 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringArray; 444 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringSet; 444 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringValueMap; 444 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringList; 444 +Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object; 444 +Landroid/app/ActivityTaskManager; 444 +Landroid/aconfig/nano/Aconfig$parsed_flag; 444 +Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap; 444 +Landroid/app/servertransaction/TopResumedActivityChangeItem; 445 +Landroid/app/LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0; 446 +Landroid/app/LoadedApk$ServiceDispatcher$RunConnection; 447 +Landroid/view/Choreographer$FrameDisplayEventReceiver; 448 +Landroid/view/inputmethod/InputMethodManager$H; 448 +Landroid/graphics/HardwareRendererObserver$$ExternalSyntheticLambda0; 448 +Landroid/view/ViewRootImpl$ViewRootHandler; 449 +Landroid/view/Choreographer$FrameHandler; 450 +Landroid/view/View$$ExternalSyntheticLambda4; 451 +Landroid/app/IActivityTaskManager; 451 +Landroid/os/AsyncTask$InternalHandler; 452 +Landroid/app/job/JobServiceEngine$JobHandler; 452 +Landroid/app/servertransaction/PendingTransactionActions$StopInfo; 453 +Landroid/os/MessageQueue; 453 +Landroid/widget/PopupWindow$PopupDecorView; 453 +Landroid/view/WindowLeaked; 454 +Landroid/app/servertransaction/ClientTransaction; 455 +Landroid/content/res/Resources; 457 +Landroid/util/Pair; 458 +Landroid/widget/Switch; 460 +Landroid/view/ViewManager; 467 +Landroid/view/accessibility/AccessibilityEventSource; 467 +Landroid/view/KeyEvent$Callback; 467 +Landroid/view/ViewParent; 467 +Landroid/graphics/drawable/Drawable$Callback; 467 +Landroid/content/pm/SigningDetails; 468 +Landroid/content/pm/FeatureInfo; 468 +Landroid/content/pm/ProviderInfo; 468 +Landroid/content/pm/PermissionInfo; 468 +Landroid/content/pm/PackageItemInfo; 469 +Landroid/content/pm/PackageInfo; 470 +Landroid/app/IActivityManager$Stub$Proxy; 473 +Lcom/android/internal/os/PowerProfile;.sPowerItemMap:Ljava/util/HashMap; 474 +Lcom/android/internal/os/PowerProfile;.sPowerArrayMap:Ljava/util/HashMap; 474 +Lcom/android/internal/os/PowerProfile;.sModemPowerProfile:Lcom/android/internal/power/ModemPowerProfile;.mPowerConstants:Landroid/util/SparseDoubleArray;.mValues:Landroid/util/SparseLongArray; 474 +Landroid/widget/HorizontalScrollView; 475 +Landroid/view/ViewTreeObserver$OnWindowFocusChangeListener; 476 +Landroid/content/res/AssetManager$AssetInputStream; 477 +Landroid/os/Parcelable; 478 +Landroid/icu/util/Calendar;.WEEK_DATA_CACHE:Landroid/icu/util/Calendar$WeekDataCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 480 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.10:Ljava/util/WeakHashMap$Entry; 482 +Landroid/view/InsetsAnimationThread; 483 +Lcom/android/internal/jank/InteractionJankMonitor$InstanceHolder; 484 +Lcom/android/internal/jank/InteractionJankMonitor; 484 +Landroid/view/View$OnSystemUiVisibilityChangeListener; 485 +Landroid/hardware/display/DisplayManager$DisplayListener; 486 +Landroid/view/View$OnApplyWindowInsetsListener; 486 +Landroid/view/Choreographer$FrameCallback; 487 +Landroid/os/Handler$Callback; 489 +Landroid/os/Build$VERSION; 490 +Landroid/view/View$OnLayoutChangeListener; 490 +Landroid/app/SharedPreferencesImpl$EditorImpl; 491 +Landroid/view/InputDevice; 492 +Landroid/preference/PreferenceManager; 492 +Landroid/os/Build; 493 +Landroid/app/ContextImpl$ApplicationContentResolver; 496 +Landroid/provider/Settings$Secure; 496 +Landroid/view/TextureView; 497 +Landroid/os/strictmode/NetworkViolation; 498 +Landroid/graphics/drawable/AnimatedVectorDrawable; 499 +Landroid/icu/util/MeasureUnit$Complexity;.MIXED:Landroid/icu/util/MeasureUnit$Complexity;.name:Ljava/lang/String; 500 +Landroid/media/MediaDrm; 500 +Lcom/android/internal/app/procstats/DumpUtils;.STATE_NAMES_CSV:[Ljava/lang/String;.12:Ljava/lang/String; 500 +Landroid/provider/DocumentsContract;.DOWNLOADS_PROVIDER_AUTHORITY:Ljava/lang/String; 500 +Landroid/annotation/SystemApi; 500 +Landroid/icu/text/MessagePattern;.argTypes:[Landroid/icu/text/MessagePattern$ArgType;.0:Landroid/icu/text/MessagePattern$ArgType;.name:Ljava/lang/String; 500 +Landroid/webkit/WebViewFactory;.sProviderLock:Ljava/lang/Object; 505 +Landroid/app/ActivityThread$H; 506 +Landroid/view/AttachedSurfaceControl$OnBufferTransformHintChangedListener; 508 +Landroid/widget/ViewFlipper; 517 +Landroid/app/IActivityTaskManager$Stub$Proxy; 519 +Landroid/app/ActivityThread$ProviderRefCount; 519 +Landroid/view/ViewRootImpl$W; 519 +Lcom/android/internal/telephony/ITelephony; 519 +Lcom/android/internal/os/PowerProfile; 521 +Landroid/view/View$VisibilityChangeForAutofillHandler; 521 +Landroid/view/View$ScrollabilityCache; 521 +Landroid/app/INotificationManager; 521 +Landroid/graphics/drawable/LevelListDrawable; 521 +Landroid/app/SharedPreferencesImpl$EditorImpl$$ExternalSyntheticLambda0; 522 +Landroid/window/SplashScreen; 526 +Landroid/media/AudioManager$OnAudioFocusChangeListener; 528 +Landroid/app/Application; 529 +Landroid/content/ContextWrapper; 530 +Landroid/view/SurfaceView; 531 +Landroid/hardware/camera2/CameraCharacteristics;.FLASH_INFO_AVAILABLE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 532 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_TARGET_FPS_RANGE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 533 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 533 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_ZOOM_RATIO:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 533 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 533 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_EXPOSURE_COMPENSATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 533 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 533 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 533 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_ENABLE_ZSL:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 533 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_TEST_PATTERN_DATA:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.JPEG_QUALITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.STATISTICS_LENS_SHADING_MAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.LENS_FOCUS_DISTANCE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_TEST_PATTERN_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.LENS_APERTURE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.STATISTICS_HOT_PIXEL_MAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_SESSION_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_REFERENCE_ILLUMINANT1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.EFV_STABILIZATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.LENS_OPTICAL_STABILIZATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.LENS_FILTER_DENSITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.EFV_PADDING_ZOOM_FACTOR:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.BLACK_LEVEL_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.STATISTICS_FACE_DETECT_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.JPEG_THUMBNAIL_SIZE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.SCALER_ROTATE_AND_CROP:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_OPTICAL_BLACK_REGIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.FLASH_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_TRIGGER:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.EFV_MAX_PADDING_ZOOM_FACTOR:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_PIXEL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_WHITE_LEVEL:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.DISTORTION_CORRECTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_CAPTURE_INTENT:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.EFV_AUTO_ZOOM:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_CALIBRATION_TRANSFORM1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_ABERRATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_COLOR_TRANSFORM1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.TONEMAP_GAMMA:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.NOISE_REDUCTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.SCALER_CROP_REGION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.TONEMAP_PRESET_CURVE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_EXPOSURE_TIME_RANGE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_POST_RAW_SENSITIVITY_BOOST:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.STATISTICS_OIS_DATA_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_FRAME_DURATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.HOT_PIXEL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_EXTENDED_SCENE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_GAINS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_COLOR_TRANSFORM2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_CALIBRATION_TRANSFORM2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.JPEG_THUMBNAIL_QUALITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_PRECAPTURE_TRIGGER:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_SENSITIVITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_EFFECT_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +[Landroid/hardware/camera2/params/MeteringRectangle; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_ANTIBANDING_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AUTOFRAMING:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.EXTENSION_STRENGTH:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_SCENE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_SETTINGS_OVERRIDE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.EFV_ROTATE_VIEWPORT:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.FLASH_STRENGTH_LEVEL:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_REFERENCE_ILLUMINANT2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PIXEL_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_VIDEO_STABILIZATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.LENS_FOCAL_LENGTH:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.JPEG_ORIENTATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.EFV_TRANSLATE_VIEWPORT:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_EXPOSURE_TIME:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.TONEMAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_TRANSFORM:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.SHADING_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CaptureRequest;.EDGE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 534 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PHYSICAL_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 535 +Landroid/util/Log; 536 +Landroid/accounts/AccountManager$20; 537 +Landroid/accounts/OnAccountsUpdateListener; 538 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.7:Ljava/util/WeakHashMap$Entry; 541 +Landroid/os/FileObserver; 544 +Landroid/widget/Space; 546 +Landroid/content/pm/ApplicationInfo; 547 +Landroid/graphics/ColorMatrix;.dexCache:Ljava/lang/Object; 548 +Landroid/text/style/CharacterStyle; 549 +Landroid/text/style/AlignmentSpan; 550 +Landroid/text/TextWatcher; 551 +Landroid/graphics/Bitmap;.sAllBitmaps:Ljava/util/WeakHashMap;.queue:Ljava/lang/ref/ReferenceQueue; 553 +Landroid/view/ViewRootImpl$$ExternalSyntheticLambda11; 554 +Landroid/app/Fragment;.sClassMap:Landroid/util/ArrayMap; 555 +Landroid/os/Bundle; 556 +Landroid/app/ActivityTaskManager;.sInstance:Landroid/util/Singleton; 557 +Landroid/content/pm/ShortcutInfo; 564 +Landroid/graphics/drawable/Icon; 565 +Landroid/os/PersistableBundle; 566 +Landroid/content/LocusId; 576 +Landroid/view/contentcapture/ContentCaptureContext; 576 +Landroid/telephony/TelephonyCallback$DisplayInfoListener; 577 +Landroid/app/Notification$Builder; 583 +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; 583 +Landroid/telephony/ims/ImsService;.CAPABILITIES_LOG_MAP:Ljava/util/Map;.table:[Ljava/lang/Object;.8:Ljava/lang/Long; 583 +Landroid/app/Notification; 584 +Landroid/app/RemoteAction; 585 +Landroid/graphics/Insets; 590 +Landroid/graphics/Rect; 591 +Lcom/android/internal/os/BackgroundThread; 592 +Landroid/widget/ViewSwitcher; 602 +Landroid/graphics/Color;.sColorNameMap:Ljava/util/HashMap;.table:[Ljava/util/HashMap$Node;.3:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 603 +Lcom/android/ims/rcs/uce/presence/pidfparser/pidf/Basic;.OPEN:Ljava/lang/String; 603 +Lcom/android/internal/os/BinderCallsStats$SettingsObserver;.SETTINGS_ENABLED_KEY:Ljava/lang/String; 603 +Landroid/icu/text/DecimalFormatSymbols;.DEF_DIGIT_STRINGS_ARRAY:[Ljava/lang/String;.3:Ljava/lang/String; 603 +Landroid/os/BatteryConsumer;.sPowerComponentNames:[Ljava/lang/String;.13:Ljava/lang/String; 603 +Landroid/os/IncidentManager;.URI_SCHEME:Ljava/lang/String; 603 +Landroid/os/AsyncTask$4; 603 +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; 603 +Landroid/provider/DocumentsContract;.PATH_SEARCH:Ljava/lang/String; 603 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.437:Ljava/lang/String; 603 +Landroid/icu/impl/units/UnitsData$Constants;.DEFAULT_USAGE:Ljava/lang/String; 604 +Lcom/android/internal/telephony/IccProvider;.ADDRESS_BOOK_COLUMN_NAMES:[Ljava/lang/String;.0:Ljava/lang/String; 604 +Landroid/text/method/DialerKeyListener; 605 +Landroid/icu/text/DecimalFormatSymbols;.DEF_DIGIT_STRINGS_ARRAY:[Ljava/lang/String;.0:Ljava/lang/String; 605 +Lcom/android/ims/rcs/uce/presence/pidfparser/pidf/Timestamp;.ELEMENT_NAME:Ljava/lang/String; 605 +Lcom/android/ims/rcs/uce/presence/pidfparser/pidf/Status;.ELEMENT_NAME:Ljava/lang/String; 605 +Landroid/os/BatteryManager;.EXTRA_SEQUENCE:Ljava/lang/String; 605 +Landroid/icu/text/MessageFormat;.typeList:[Ljava/lang/String;.5:Ljava/lang/String; 605 +Landroid/provider/Telephony$ThreadsColumns;.ERROR:Ljava/lang/String; 605 +Lcom/android/internal/os/RailStats;.WIFI_SUBSYSTEM:Ljava/lang/String; 605 +Landroid/app/NotificationChannel;.TAG_CHANNEL:Ljava/lang/String; 605 +Landroid/app/NotificationChannel;.EDIT_LAUNCHER:Ljava/lang/String; 605 +Lcom/android/ims/ImsUt;.KEY_ACTION:Ljava/lang/String; 605 +Landroid/view/textclassifier/TextClassifier;.TYPE_URL:Ljava/lang/String; 605 +Landroid/provider/Telephony$BaseMmsColumns;.START:Ljava/lang/String; 605 +Landroid/icu/impl/ValidIdentifiers$Datatype;.region:Landroid/icu/impl/ValidIdentifiers$Datatype;.name:Ljava/lang/String; 605 +Landroid/icu/impl/ZoneMeta;.REGION_CACHE:Landroid/icu/impl/ICUCache; 608 +Landroid/widget/TextView;.TEMP_RECTF:Landroid/graphics/RectF; 609 +Landroid/app/Activity; 611 +Landroid/text/method/LinkMovementMethod; 614 +Landroid/net/Uri; 615 +Landroid/app/PendingIntent; 617 +Landroid/security/net/config/UserCertificateSource$NoPreloadHolder; 618 +Landroid/net/Uri$PathPart;.EMPTY:Landroid/net/Uri$PathPart; 619 +Landroid/content/Intent; 620 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.197:Landroid/os/PersistableBundle; 622 +Landroid/graphics/HardwareRenderer; 624 +Landroid/icu/util/TimeZone; 625 +Landroid/telephony/TelephonyRegistryManager; 627 +Landroid/telecom/TelecomManager; 628 +Landroid/telephony/TelephonyCallback$ServiceStateListener; 629 +Landroid/telephony/TelephonyCallback$RadioPowerStateListener; 630 +Landroid/widget/ActionMenuView; 631 +Landroid/widget/ActionMenuPresenter$OverflowMenuButton; 631 +Landroid/widget/Toolbar; 631 +Lcom/android/internal/widget/ActionBarContainer; 632 +Lcom/android/internal/widget/ActionBarContainer$ActionBarBackgroundDrawable; 632 +Lcom/android/internal/widget/ActionBarContextView; 632 +Lcom/android/internal/widget/ActionBarOverlayLayout; 632 +Landroid/graphics/drawable/AdaptiveIconDrawable; 633 +Landroid/widget/ImageButton; 634 +Landroid/widget/Button; 635 +Landroid/view/AbsSavedState$1; 636 +Landroid/app/FragmentManagerState; 637 +Landroid/view/View$BaseSavedState; 638 +Landroid/graphics/drawable/ShapeDrawable; 639 +Lcom/android/internal/telephony/WspTypeDecoder;.WELL_KNOWN_MIME_TYPES:Ljava/util/HashMap;.table:[Ljava/util/HashMap$Node;.54:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 640 +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; 640 +Landroid/widget/MultiAutoCompleteTextView; 642 +Landroid/speech/RecognitionListener; 642 +Landroid/widget/ToggleButton; 643 +Landroid/widget/AutoCompleteTextView; 644 +Landroid/widget/RadioButton; 645 +Landroid/widget/CheckBox; 646 +Landroid/view/View$OnGenericMotionListener; 647 +Landroid/os/UserHandle; 648 +Landroid/app/servertransaction/ResumeActivityItem; 655 +Landroid/app/servertransaction/ActivityRelaunchItem; 655 +Landroid/app/servertransaction/ObjectPool;.sPoolMap:Ljava/util/Map; 655 +Landroid/text/style/URLSpan; 656 +Landroid/icu/util/ULocale$AvailableType;.DEFAULT:Landroid/icu/util/ULocale$AvailableType;.name:Ljava/lang/String; 656 +Landroid/icu/util/CodePointMap$RangeOption;.NORMAL:Landroid/icu/util/CodePointMap$RangeOption;.name:Ljava/lang/String; 658 +Landroid/security/keystore2/AndroidKeyStoreProvider; 676 +Landroid/security/keystore2/KeyStoreCryptoOperationUtils; 677 +Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener; 678 +Landroid/view/View;.sNextGeneratedId:Ljava/util/concurrent/atomic/AtomicInteger; 679 +Landroid/icu/impl/number/range/StandardPluralRanges; 680 +Landroid/icu/impl/PluralRulesLoader;.loader:Landroid/icu/impl/PluralRulesLoader; 680 +Landroid/icu/impl/PluralRulesLoader;.loader:Landroid/icu/impl/PluralRulesLoader;.pluralRulesCache:Ljava/util/Map; 680 +Landroid/icu/text/PluralRules$Operand; 680 +Landroid/icu/impl/StandardPlural; 681 +Landroid/webkit/JavascriptInterface; 682 +Landroid/content/res/AssetManager; 684 +Ljavax/sip/header/ContentEncodingHeader;.NAME:Ljava/lang/String; 685 +Landroid/view/View$OnClickListener; 685 +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; 685 +Landroid/widget/CompoundButton; 685 +Landroid/view/accessibility/AccessibilityManager;.sInstanceSync:Ljava/lang/Object; 686 +Landroid/webkit/CookieManager; 687 +Landroid/icu/text/NFRule;.ZERO:Ljava/lang/Long; 689 +Landroid/app/AppOpsManager$OnOpActiveChangedListener; 691 +Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mMap:Landroid/util/ArrayMap; 692 +Landroid/os/RemoteException; 693 +Landroid/content/pm/PackageManager$OnChecksumsReadyListener; 694 +Landroid/content/pm/Checksum$Type; 695 +Landroid/view/InputEvent;.mNextSeq:Ljava/util/concurrent/atomic/AtomicInteger; 696 +Landroid/view/MotionEvent; 697 +Landroid/widget/ScrollView; 698 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.10:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mSkips:[J 702 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.queue:Ljava/lang/ref/ReferenceQueue; 703 +Landroid/widget/ProgressBar; 704 +Landroid/animation/LayoutTransition; 706 +Landroid/animation/PropertyValuesHolder$IntPropertyValuesHolder;.sJNISetterPropertyMap:Ljava/util/HashMap; 707 +Lorg/apache/http/params/HttpParams; 711 +Landroid/app/NotificationChannelGroup; 712 +Landroid/content/pm/ParceledListSlice; 712 +Landroid/os/vibrator/StepSegment; 713 +Lcom/android/internal/util/LatencyTracker$SLatencyTrackerHolder; 714 +Lcom/android/internal/util/LatencyTracker; 714 +Landroid/app/Application$ActivityLifecycleCallbacks; 715 +Landroid/os/Messenger; 716 +Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AE_AVAILABLE_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_ACTIVE_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraCharacteristics;.INFO_SESSION_CONFIGURATION_QUERY_VERSION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_SHADING_MAP_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CaptureResult;.SENSOR_TIMESTAMP:Landroid/hardware/camera2/CaptureResult$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AWB_AVAILABLE_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraDevice$StateCallback; 720 +Landroid/hardware/camera2/impl/CameraMetadataNative; 720 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STREAM_USE_CASES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_ZOOM_RATIO_RANGE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/util/Size; 720 +Landroid/hardware/camera2/CameraCaptureSession$StateCallback; 720 +Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AF_AVAILABLE_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_PARTIAL_RESULT_COUNT:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 720 +Landroid/hardware/camera2/CameraCaptureSession$CaptureCallback; 720 +Landroid/util/Range; 720 +Landroid/widget/ListView; 721 +Landroid/widget/AbsListView; 721 +Landroid/transition/Explode; 723 +Landroid/text/HtmlToSpannedConverter$Font; 724 +Landroid/text/Html$TagHandler; 725 +Lcom/android/internal/telephony/cdnr/CarrierDisplayNameResolver;.EF_SOURCE_PRIORITY:Ljava/util/List;.a:[Ljava/lang/Object;.2:Ljava/lang/Integer; 725 +Landroid/telephony/ims/feature/MmTelFeature$1; 726 +Landroid/telephony/ims/stub/ImsConfigImplBase$ImsConfigStub; 726 +Landroid/telephony/ims/ImsUtListener; 726 +Landroid/telephony/ims/stub/ImsRegistrationImplBase$1; 726 +Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray;.mKeys:[I 727 +Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray; 728 +Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 728 +Landroid/telephony/ims/aidl/IImsConfig$Stub$Proxy; 729 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipMessageResponse; 730 +Landroid/timezone/TimeZoneFinder; 730 +Landroid/telephony/TelephonyRegistryManager$3; 730 +Lcom/android/internal/telephony/DeviceStateMonitor; 730 +Lcom/android/internal/telephony/imsphone/ImsPhone; 730 +Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker; 730 +Lcom/android/internal/telephony/cat/CatService; 730 +Landroid/net/NetworkPolicyManager$SubscriptionCallbackProxy; 730 +Landroid/telephony/ModemActivityInfo; 730 +Lcom/android/internal/telephony/InboundSmsHandler$NewMessageNotificationActionReceiver; 730 +Landroid/icu/impl/ZoneMeta;.SYSTEM_ZONE_CACHE:Landroid/icu/impl/ZoneMeta$SystemTimeZoneCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 730 +Lcom/android/internal/telephony/TelephonyDevController; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$GbaEvent; 730 +Lcom/android/internal/telephony/RadioInterfaceCapabilityController; 730 +Lcom/android/internal/telephony/SmsBroadcastUndelivered; 730 +Landroid/telephony/data/ApnSetting;.APN_TYPE_INT_MAP:Ljava/util/Map; 730 +Lcom/android/internal/telephony/SmsStorageMonitor$1; 730 +Lcom/android/internal/telephony/CellBroadcastServiceManager; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularDataServiceSwitch; 730 +Lcom/android/internal/telephony/StateMachine$SmHandler; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierRoamingSatelliteControllerStats; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.673:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/NetworkTypeController$1; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mDisplayInfoRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/CarrierActionAgent; 730 +Lcom/android/internal/telephony/uicc/UiccCarrierPrivilegeRules; 730 +Lcom/android/internal/telephony/MultiSimSettingController; 730 +Lcom/android/internal/telephony/GsmCdmaCallTracker; 730 +Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler$GsmCbTestBroadcastReceiver; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteController; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1289:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/IntentBroadcaster$1; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.483:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$PresenceNotifyEvent; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSosMessageRecommender; 730 +Landroid/telephony/CellSignalStrengthWcdma; 730 +Lcom/android/ims/rcs/uce/eab/EabProvider; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierRoamingSatelliteSession; 730 +Lcom/android/internal/telephony/TelephonyComponentFactory; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mRingingCalls:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/ims/ImsResolver$2; 730 +Lcom/android/internal/telephony/emergency/EmergencyNumberTracker; 730 +Landroid/timezone/TelephonyLookup; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$UnavailableState; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.363:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/ProxyController; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteOutgoingDatagram; 730 +Lcom/android/i18n/timezone/TimeZoneFinder; 730 +Lcom/android/internal/telephony/SimActivationTracker; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteProvision; 730 +Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler$CdmaScpTestBroadcastReceiver; 730 +Lcom/android/internal/telephony/ServiceStateTracker; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$AvailableState; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsClientProvisioningStats; 730 +Lcom/android/internal/telephony/SmsApplication; 730 +Lcom/android/internal/telephony/TelephonyDevController;.mModems:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/SimActivationTracker$1; 730 +Lcom/android/internal/telephony/emergency/EmergencyNumberTracker$1; 730 +Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequestsV2; 730 +Lcom/android/internal/telephony/AppSmsManager; 730 +Landroid/telephony/ims/ProvisioningManager$Callback$CallbackBinder; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.789:[Ljava/lang/String; 730 +Lcom/android/i18n/timezone/TelephonyLookup; 730 +Lcom/android/internal/telephony/CarrierServiceBindHelper$CarrierServicePackageMonitor; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.549:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mInCallVoicePrivacyOffRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler$CdmaCbTestBroadcastReceiver; 730 +Lcom/android/internal/telephony/IntentBroadcaster; 730 +Landroid/telephony/ims/RegistrationManager$RegistrationCallback$RegistrationBinder; 730 +Lcom/android/internal/telephony/GsmCdmaCallTracker$1; 730 +Lcom/android/internal/telephony/uicc/UiccProfile$2; 730 +Landroid/telephony/ims/ImsMmTelManager$CapabilityCallback$CapabilityBinder; 730 +Landroid/telephony/CellSignalStrengthGsm; 730 +Landroid/os/Handler$MessengerImpl; 730 +Lcom/android/internal/telephony/DisplayInfoController; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationStats; 730 +Lcom/android/internal/telephony/DeviceStateMonitor$3; 730 +Lcom/android/internal/telephony/RIL;.sRilTimeHistograms:Landroid/util/SparseArray; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.469:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/PhoneConfigurationManager; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.349:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/TelephonyDevController;.mSims:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$IncomingSms; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$UnmeteredNetworks; 730 +Lcom/android/internal/telephony/euicc/EuiccCardController; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mMmiCompleteRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$DataNetworkValidation; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$BindingState; 730 +Lcom/android/internal/telephony/uicc/UiccPkcs15$Pkcs15Selector; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.23:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/SomeArgs; 730 +Lcom/android/internal/telephony/SmsStorageMonitor; 730 +Lcom/android/internal/telephony/SmsApplication$SmsPackageMonitor; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierIdMismatch; 730 +Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mSuppServiceFailedRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall; 730 +Lcom/android/internal/telephony/metrics/TelephonyMetrics; 730 +Lcom/android/internal/telephony/ims/ImsResolver$3; 730 +Lcom/android/internal/telephony/uicc/UiccProfile; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteAccessController; 730 +Lcom/android/internal/telephony/NitzStateMachine; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$DisconnectedState; 730 +Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher; 730 +Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState$15; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.833:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/SmsUsageMonitor; 730 +Lcom/android/internal/telephony/euicc/EuiccCardController$SimSlotStatusChangedBroadcastReceiver; 730 +Lcom/android/internal/telephony/ims/ImsServiceController$1; 730 +Lcom/android/internal/telephony/satellite/SatelliteModemInterface; 730 +Lcom/android/internal/telephony/CarrierResolver$2; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequests; 730 +Lcom/android/internal/telephony/MccTable; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipDelegateStats; 730 +Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 730 +Lcom/android/ims/FeatureConnector$1; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1443:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/ims/ImsResolver$1; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState; 730 +Lcom/android/internal/telephony/security/NullCipherNotifier; 730 +Lcom/android/internal/telephony/SMSDispatcher$1; 730 +Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo; 730 +Lcom/android/internal/telephony/LocaleTracker$1; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mTtyModeReceivedRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallRatUsage; 730 +Landroid/os/AsyncResult; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mDisconnectRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteConfigUpdater; 730 +Lcom/android/internal/telephony/ims/ImsResolver; 730 +Lcom/android/internal/telephony/ServiceStateTracker$1; 730 +Lcom/android/internal/telephony/PhoneFactory; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager; 730 +Landroid/telephony/CellSignalStrengthTdscdma; 730 +Landroid/telephony/emergency/EmergencyNumber; 730 +Lcom/android/internal/telephony/GsmCdmaPhone; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportSession; 730 +Landroid/app/timezonedetector/TimeZoneDetector; 730 +Landroid/telephony/ims/aidl/IImsServiceController$Stub$Proxy; 730 +Lcom/android/internal/telephony/TelephonyTester$1; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$EmergencyNumbersInfo; 730 +Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyServiceState$NetworkRegistrationInfo; 730 +Lcom/android/internal/telephony/CarrierServiceBindHelper$1; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mHandlerMap:Ljava/util/HashMap; 730 +Lcom/android/internal/telephony/IccPhoneBookInterfaceManager; 730 +Lcom/android/internal/telephony/CarrierPrivilegesTracker$1; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1173:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$DataCallSession; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mForegroundCalls:Ljava/util/ArrayList; 730 +Landroid/telephony/TelephonyLocalConnection; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$1; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerListenerEvent; 730 +Landroid/telephony/ModemInfo; 730 +Lcom/android/internal/telephony/CommandException$Error;.INVALID_SIM_STATE:Lcom/android/internal/telephony/CommandException$Error; 730 +Lcom/android/internal/telephony/CommandException; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallSession; 730 +Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker; 730 +Lcom/android/internal/telephony/euicc/EuiccController; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSession; 730 +Landroid/telephony/CellSignalStrengthLte; 730 +Lcom/android/internal/telephony/CarrierActionAgent$1; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingShortCodeSms; 730 +Lcom/android/internal/telephony/satellite/PointingAppController; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteIncomingDatagram; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationFeatureTagStats; 730 +Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker$2; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.33:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall; 730 +Lcom/android/internal/telephony/RILRequest; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState$5; 730 +Landroid/telephony/BarringInfo$BarringServiceInfo; 730 +Lcom/android/internal/telephony/IWapPushManager; 730 +Lcom/android/internal/telephony/SmsDispatchersController; 730 +Lcom/android/internal/telephony/uicc/UiccController; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularServiceState; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingSms; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteEntitlement; 730 +Lcom/android/internal/telephony/util/NotificationChannelController$1; 730 +Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray;.mKeys:[I 730 +Lcom/android/internal/telephony/IccSmsInterfaceManager; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportFeatureTagStats; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mInCallVoicePrivacyOnRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/uicc/UiccProfile$4; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$UceEventStats; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerEvent; 730 +Lcom/android/internal/telephony/RadioConfig; 730 +Lcom/android/internal/telephony/PackageChangeReceiver; 730 +Lcom/android/internal/telephony/PhoneSubInfoController; 730 +Lcom/android/internal/telephony/euicc/EuiccConnector$EuiccPackageMonitor; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationTermination; 730 +Lcom/android/internal/telephony/uicc/PinStorage$1; 730 +Lcom/android/internal/telephony/SmsBroadcastUndelivered$1; 730 +Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager; 730 +Lcom/android/internal/telephony/CarrierKeyDownloadManager$3; 730 +Lcom/android/internal/telephony/NetworkTypeController$DefaultState; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsAcsProvisioningStats; 730 +Landroid/net/TelephonyNetworkSpecifier; 730 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1459:[Ljava/lang/String; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mSignalInfoRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationServiceDescStats; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mBackgroundCalls:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mPhones:Ljava/util/ArrayList; 730 +Lcom/android/internal/telephony/LocaleTracker; 730 +Lcom/android/internal/telephony/RilWakelockInfo; 730 +Landroid/telephony/CellSignalStrengthNr; 730 +Landroid/telephony/CellSignalStrengthCdma; 730 +Landroid/telephony/NetworkService; 731 +Landroid/util/Log$TerribleFailure; 732 +Lcom/android/internal/os/RuntimeInit$KillApplicationHandler; 733 +Landroid/content/res/ResourcesKey; 734 +Landroid/app/ResourcesManager; 734 +Landroid/widget/HorizontalScrollView$SavedState; 736 +Landroid/widget/AbsSpinner$SavedState; 736 +Landroid/widget/Spinner$SavedState; 736 +Lcom/android/internal/transition/EpicenterTranslateClipReveal; 737 +Landroid/transition/TransitionInflater;.sConstructors:Landroid/util/ArrayMap; 737 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.15:Ljava/lang/String; 738 +Landroid/view/ThreadedRenderer;.OVERDRAW_PROPERTY_SHOW:Ljava/lang/String; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.141:Ljava/lang/String; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.35:Ljava/lang/String; 738 +Landroid/text/Html$ImageGetter; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.80:Ljava/lang/String; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.224:Ljava/lang/String; 738 +Landroid/webkit/ConsoleMessage$MessageLevel;.WARNING:Landroid/webkit/ConsoleMessage$MessageLevel; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.118:Ljava/lang/String; 738 +Landroid/webkit/ConsoleMessage$MessageLevel;.LOG:Landroid/webkit/ConsoleMessage$MessageLevel; 738 +Landroid/webkit/ConsoleMessage$MessageLevel;.DEBUG:Landroid/webkit/ConsoleMessage$MessageLevel; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.221:Ljava/lang/String; 738 +Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.9:Ljava/lang/String; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.127:Ljava/lang/String; 738 +Landroid/window/ImeOnBackInvokedDispatcher;.RESULT_KEY_PRIORITY:Ljava/lang/String; 738 +Landroid/util/AndroidRuntimeException; 738 +Lcom/android/internal/app/procstats/DumpUtils;.STATE_TAGS:[Ljava/lang/String;.14:Ljava/lang/String; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.6:Ljava/lang/String; 738 +Landroid/webkit/ConsoleMessage$MessageLevel;.TIP:Landroid/webkit/ConsoleMessage$MessageLevel; 738 +Landroid/webkit/ConsoleMessage$MessageLevel;.ERROR:Landroid/webkit/ConsoleMessage$MessageLevel; 738 +Landroid/opengl/GLSurfaceView; 738 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.237:Ljava/lang/String; 738 +Landroid/app/servertransaction/LaunchActivityItem; 739 +Landroid/app/backup/BackupManager; 750 +Landroid/content/pm/PackageManager$NameNotFoundException; 751 +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; 752 +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; 752 +Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader; 752 +Landroid/icu/util/ULocale$AliasReplacer; 753 +Landroid/widget/MediaController$MediaPlayerControl; 755 +Landroid/graphics/Point; 759 +Landroid/content/ContentValues; 759 +Landroid/opengl/GLSurfaceView$Renderer; 760 +Landroid/opengl/GLSurfaceView;.sGLThreadManager:Landroid/opengl/GLSurfaceView$GLThreadManager; 761 +Landroid/text/StaticLayout; 762 +Landroid/animation/AnimationHandler$AnimationFrameCallbackProvider; 762 +Landroid/text/method/DigitsKeyListener;.sLocaleInstanceCache:Ljava/util/HashMap; 764 +Landroid/graphics/Path$Op; 765 +Landroid/text/method/QwertyKeyListener; 765 +Landroid/app/StackTrace; 765 +Landroid/icu/text/BreakIterator;.iterCache:[Landroid/icu/impl/CacheValue; 766 +Landroid/view/View$OnHoverListener; 767 +Landroid/content/res/ColorStateList; 767 +Landroid/security/IKeyChainService; 768 +Landroid/app/Notification$MessagingStyle; 769 +Landroid/content/ContentProviderClient; 773 +Landroid/os/UserHandle;.sExtraUserHandleCache:Landroid/util/SparseArray; 773 +Landroid/content/ContentProvider$PipeDataWriter; 774 +Landroid/os/strictmode/UnsafeIntentLaunchViolation; 774 +Landroid/content/AsyncQueryHandler; 776 +Landroid/app/Activity$$ExternalSyntheticLambda0; 777 +Landroid/widget/CheckedTextView; 777 +Landroid/graphics/PorterDuff$Mode;.SRC_IN:Landroid/graphics/PorterDuff$Mode; 778 +Landroid/graphics/drawable/InsetDrawable; 779 +Landroid/view/OrientationEventListener; 781 +Landroid/hardware/camera2/CameraCharacteristics;.INFO_SUPPORTED_HARDWARE_LEVEL:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 782 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_ORIENTATION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 783 +Landroid/hardware/camera2/CameraCharacteristics;.INFO_DEVICE_STATE_ORIENTATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 783 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.9:Ljava/util/WeakHashMap$Entry; 785 +Landroid/media/MediaRouter$VolumeChangeReceiver; 787 +Landroid/media/MediaRouter$WifiDisplayStatusChangedReceiver; 787 +Landroid/media/MediaRouter; 788 +Landroid/hardware/SensorPrivacyManager; 789 +Landroid/os/storage/StorageManager; 790 +Landroid/telephony/SubscriptionManager; 791 +Landroid/view/textservice/TextServicesManager; 791 +Landroid/os/SystemConfigManager; 791 +Landroid/view/LayoutInflater; 791 +Landroid/hardware/input/InputManager; 791 +Landroid/credentials/CredentialManager; 791 +Landroid/permission/PermissionControllerManager; 791 +Landroid/net/NetworkPolicyManager; 791 +Landroid/security/attestationverification/AttestationVerificationManager; 791 +Landroid/app/contextualsearch/ContextualSearchManager; 791 +Landroid/app/wearable/WearableSensingManager; 791 +Landroid/app/people/PeopleManager; 791 +Landroid/net/wifi/sharedconnectivity/app/SharedConnectivityManager; 791 +Landroid/media/session/MediaSessionManager; 791 +Landroid/provider/E2eeContactKeysManager; 791 +Landroid/view/contentcapture/ContentCaptureManager; 791 +Landroid/app/contentsuggestions/ContentSuggestionsManager; 791 +Landroid/os/HardwarePropertiesManager; 791 +Landroid/app/admin/DevicePolicyManager; 791 +Landroid/view/translation/TranslationManager; 791 +Landroid/view/autofill/AutofillManager; 791 +Landroid/net/vcn/VcnManager; 791 +Landroid/service/persistentdata/PersistentDataBlockManager; 791 +Landroid/view/textclassifier/TextClassificationManager; 791 +Landroid/media/tv/tunerresourcemanager/TunerResourceManager; 791 +Landroid/os/RecoverySystem; 791 +Landroid/os/strictmode/Violation; 793 +Lcom/android/internal/telephony/uicc/asn1/Asn1Node;.EMPTY_NODE_LIST:Ljava/util/List; 795 +Landroid/os/strictmode/CredentialProtectedWhileLockedViolation; 796 +Landroid/net/metrics/DhcpClientEvent; 797 +Landroid/icu/impl/CharacterPropertiesImpl;.inclusions:[Landroid/icu/text/UnicodeSet; 798 +Landroid/content/AttributionSource; 800 +Landroid/database/sqlite/SQLiteException; 801 +Lcom/android/ims/ImsManager;.TRUE:Ljava/lang/String; 802 +Landroid/graphics/Matrix; 803 +Landroid/graphics/RectF; 803 +Landroid/os/Parcel$LazyValue; 803 +Landroid/telephony/TelephonyCallback$DataEnabledListener; 804 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.6:Ljava/util/WeakHashMap$Entry; 805 +Landroid/service/media/MediaBrowserService$ServiceState; 806 +Landroid/graphics/drawable/PictureDrawable; 806 +Landroid/content/pm/PackageManager; 812 +Landroid/window/IWindowContainerToken$Stub$Proxy; 813 +Landroid/app/UriGrantsManager;.IUriGrantsManagerSingleton:Landroid/util/Singleton; 814 +Landroid/provider/FontsContract;.sTypefaceCache:Landroid/util/LruCache;.map:Ljava/util/LinkedHashMap; 816 +Landroid/provider/FontsContract;.sTypefaceCache:Landroid/util/LruCache; 816 +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; 817 +Landroid/app/Service; 818 +Landroid/app/ReceiverRestrictedContext; 819 +Landroid/util/proto/ProtoStream;.FIELD_TYPE_NAMES:[Ljava/lang/String;.10:Ljava/lang/String; 820 +Lorg/apache/http/conn/ssl/SSLSocketFactory$NoPreloadHolder; 820 +Landroid/app/LoadedApk$WarningContextClassLoader; 820 +Ljavax/sip/header/PriorityHeader;.NORMAL:Ljava/lang/String; 820 +Lcom/android/internal/telephony/euicc/EuiccController;.EXTRA_OPERATION:Ljava/lang/String; 820 +Landroid/widget/ViewAnimator; 820 +Landroid/security/keystore/KeyGenParameterSpec; 821 +Landroid/text/style/StyleSpan; 821 +Landroid/util/SparseIntArray; 823 +Landroid/database/ContentObserver; 823 diff --git a/core/api/current.txt b/core/api/current.txt index b95295cd4ad9..a8b9e331de2a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -11240,6 +11240,7 @@ package android.content { method public void removeCategory(String); method public void removeExtra(String); method public void removeFlags(int); + method @FlaggedApi("android.security.prevent_intent_redirect") public void removeLaunchSecurityProtection(); method @NonNull public android.content.Intent replaceExtras(@NonNull android.content.Intent); method @NonNull public android.content.Intent replaceExtras(@Nullable android.os.Bundle); method public android.content.ComponentName resolveActivity(@NonNull android.content.pm.PackageManager); @@ -61760,6 +61761,7 @@ package android.window { method public float getTouchX(); method public float getTouchY(); field public static final int EDGE_LEFT = 0; // 0x0 + field @FlaggedApi("com.android.window.flags.predictive_back_swipe_edge_none_api") public static final int EDGE_NONE = 2; // 0x2 field public static final int EDGE_RIGHT = 1; // 0x1 } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 1ff8c510b6bf..79bea0188308 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1892,8 +1892,7 @@ package android.hardware.soundtrigger { } @FlaggedApi("android.media.soundtrigger.manager_api") public static final class SoundTrigger.RecognitionConfig implements android.os.Parcelable { - ctor @Deprecated public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[], int); - ctor public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[]); + ctor @Deprecated public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[]); } public static class SoundTrigger.RecognitionEvent { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e7f4dbc24022..b0a8b1b2dbf3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -237,6 +237,7 @@ import com.android.internal.os.DebugStore; import com.android.internal.os.RuntimeInit; import com.android.internal.os.SafeZipPathValidatorCallback; import com.android.internal.os.SomeArgs; +import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.DecorView; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; @@ -2680,7 +2681,10 @@ public final class ActivityThread extends ClientTransactionHandler handleUnstableProviderDied((IBinder)msg.obj, false); break; case REQUEST_ASSIST_CONTEXT_EXTRAS: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "handleRequestAssistContextExtras"); handleRequestAssistContextExtras((RequestAssistContextExtras)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case TRANSLUCENT_CONVERSION_COMPLETE: handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1); @@ -7675,6 +7679,16 @@ public final class ActivityThread extends ClientTransactionHandler } } }); + + // Register callback to report native memory metrics post GC cleanup + if (Flags.reportPostgcMemoryMetrics() && + com.android.libcore.readonly.Flags.postCleanupApis()) { + VMRuntime.addPostCleanupCallback(new Runnable() { + @Override public void run() { + MetricsLoggerWrapper.logPostGcMemorySnapshot(); + } + }); + } } @UnsupportedAppUsage diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index ed6b85125e66..fb5a12b49921 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -132,6 +132,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.Immutable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; +import com.android.internal.pm.RoSystemFeatures; import com.android.internal.util.UserIcons; import dalvik.system.VMRuntime; @@ -822,6 +823,16 @@ public class ApplicationPackageManager extends PackageManager { @Override public Boolean recompute(HasSystemFeatureQuery query) { try { + // As an optimization, check first to see if the feature was defined at + // compile-time as either available or unavailable. + // TODO(b/203143243): Consider hoisting this optimization out of the cache + // after the trunk stable (build) flag has soaked and more features are + // defined at compile-time. + Boolean maybeHasSystemFeature = + RoSystemFeatures.maybeHasFeature(query.name, query.version); + if (maybeHasSystemFeature != null) { + return maybeHasSystemFeature.booleanValue(); + } return ActivityThread.currentActivityThread().getPackageManager(). hasSystemFeature(query.name, query.version); } catch (RemoteException e) { diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index e4d3baa2f05a..acedef0c7788 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -21,11 +21,13 @@ import static android.text.TextUtils.formatSimple; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; @@ -78,10 +80,15 @@ public class PropertyInvalidatedCache<Query, Result> { public abstract @Nullable R apply(@NonNull Q query); /** - * Return true if a query should not use the cache. The default implementation - * always uses the cache. + * Return true if a query should not use the cache. The default implementation returns true + * if the process UID differs from the calling UID. This is to prevent a binder caller from + * reading a cached value created due to a different binder caller, when processes are + * caching on behalf of other processes. */ public boolean shouldBypassCache(@NonNull Q query) { + if(android.multiuser.Flags.propertyInvalidatedCacheBypassMismatchedUids()) { + return Binder.getCallingUid() != Process.myUid(); + } return false; } }; diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index e882bb564db9..081ce31e0886 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -345,6 +345,15 @@ public class TaskInfo { */ public AppCompatTaskInfo appCompatTaskInfo = AppCompatTaskInfo.create(); + /** + * The top activity's main window frame if it doesn't match the top activity bounds. + * {@code null}, otherwise. + * + * @hide + */ + @Nullable + public Rect topActivityMainWindowFrame; + TaskInfo() { // Do nothing } @@ -477,7 +486,8 @@ public class TaskInfo { && Objects.equals(capturedLink, that.capturedLink) && capturedLinkTimestamp == that.capturedLinkTimestamp && requestedVisibleTypes == that.requestedVisibleTypes - && appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo); + && appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo) + && Objects.equals(topActivityMainWindowFrame, that.topActivityMainWindowFrame); } /** @@ -553,6 +563,7 @@ public class TaskInfo { capturedLinkTimestamp = source.readLong(); requestedVisibleTypes = source.readInt(); appCompatTaskInfo = source.readTypedObject(AppCompatTaskInfo.CREATOR); + topActivityMainWindowFrame = source.readTypedObject(Rect.CREATOR); } /** @@ -606,6 +617,7 @@ public class TaskInfo { dest.writeLong(capturedLinkTimestamp); dest.writeInt(requestedVisibleTypes); dest.writeTypedObject(appCompatTaskInfo, flags); + dest.writeTypedObject(topActivityMainWindowFrame, flags); } @Override @@ -649,6 +661,7 @@ public class TaskInfo { + " capturedLinkTimestamp=" + capturedLinkTimestamp + " requestedVisibleTypes=" + requestedVisibleTypes + " appCompatTaskInfo=" + appCompatTaskInfo + + " topActivityMainWindowFrame=" + topActivityMainWindowFrame + "}"; } } diff --git a/core/java/android/app/metrics.aconfig b/core/java/android/app/metrics.aconfig new file mode 100644 index 000000000000..488f1c71990b --- /dev/null +++ b/core/java/android/app/metrics.aconfig @@ -0,0 +1,10 @@ +package: "android.app" +container: "system" + +flag { + namespace: "system_performance" + name: "report_postgc_memory_metrics" + is_exported: false + description: "Controls whether to report memory metrics post GC cleanup" + bug: "331243037" +} diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 37fa9a26b91c..1d4c18f6a62c 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -286,4 +286,11 @@ flag { namespace: "systemui" description: "Adds logging for notification/modes backup and restore events" bug: "289524803" -}
\ No newline at end of file +} + +flag { + name: "notification_classification_ui" + namespace: "systemui" + description: "Adds UI for NAS classification of notifications" + bug: "367996732" +} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index f71952849872..e8cec70ef6a8 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -20,6 +20,7 @@ import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_AC import static android.content.ContentProvider.maybeAddUserId; import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.security.Flags.FLAG_FRP_ENFORCEMENT; +import static android.security.Flags.FLAG_PREVENT_INTENT_REDIRECT; import static android.security.Flags.preventIntentRedirect; import android.Manifest; @@ -40,7 +41,10 @@ import android.app.Activity; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.StatusBarManager; +import android.app.compat.CompatChanges; import android.bluetooth.BluetoothDevice; +import android.compat.annotation.ChangeId; +import android.compat.annotation.Overridable; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -670,6 +674,11 @@ import java.util.TimeZone; public class Intent implements Parcelable, Cloneable { private static final String TAG = "Intent"; + /** @hide */ + @ChangeId + @Overridable + public static final long ENABLE_PREVENT_INTENT_REDIRECT = 29076063L; + private static final String ATTR_ACTION = "action"; private static final String TAG_CATEGORIES = "categories"; private static final String ATTR_CATEGORY = "category"; @@ -12240,7 +12249,7 @@ public class Intent implements Parcelable, Cloneable { * @hide */ public void collectExtraIntentKeys() { - if (!preventIntentRedirect()) return; + if (!isPreventIntentRedirectEnabled()) return; if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) { for (String key : mExtras.keySet()) { @@ -12257,6 +12266,14 @@ public class Intent implements Parcelable, Cloneable { } } + /** + * @hide + */ + public static boolean isPreventIntentRedirectEnabled() { + return preventIntentRedirect() && CompatChanges.isChangeEnabled( + ENABLE_PREVENT_INTENT_REDIRECT); + } + /** @hide */ public void checkCreatorToken() { if (mExtras == null) return; @@ -12281,6 +12298,20 @@ public class Intent implements Parcelable, Cloneable { mExtras.setIsIntentExtra(); } + /** + * When an intent comes from another app or component as an embedded extra intent, the system + * creates a token to identify the creator of this foreign intent. If this token is missing or + * invalid, the system will block the launch of this intent. If it contains a valid token, the + * system will perform verification against the creator to block launching target it has no + * permission to launch or block it from granting URI access to the tagert it cannot access. + * This method provides a way to opt out this feature. + */ + @FlaggedApi(FLAG_PREVENT_INTENT_REDIRECT) + public void removeLaunchSecurityProtection() { + mExtendedFlags &= ~EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN; + removeCreatorTokenInfo(); + } + public void writeToParcel(Parcel out, int flags) { out.writeString8(mAction); Uri.writeToParcel(out, mData); @@ -12331,7 +12362,7 @@ public class Intent implements Parcelable, Cloneable { out.writeInt(0); } - if (preventIntentRedirect()) { + if (isPreventIntentRedirectEnabled()) { if (mCreatorTokenInfo == null) { out.writeInt(0); } else { @@ -12398,7 +12429,7 @@ public class Intent implements Parcelable, Cloneable { mOriginalIntent = new Intent(in); } - if (preventIntentRedirect()) { + if (isPreventIntentRedirectEnabled()) { if (in.readInt() != 0) { mCreatorTokenInfo = new CreatorTokenInfo(); mCreatorTokenInfo.mCreatorToken = in.readStrongBinder(); diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index 5b38942d468d..5f439b1dcab9 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -342,3 +342,11 @@ flag { bug: "292261144" is_fixed_read_only: true } + +flag { + name: "change_launcher_badging" + namespace: "package_manager_service" + description: "Feature flag to introduce a new way to change the launcher badging." + bug: "364760703" + is_fixed_read_only: true +} diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index 3c4307c63cf7..0d583dedeb74 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -48,8 +48,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Static utility methods for dealing with databases and {@link Cursor}s. @@ -1577,49 +1575,6 @@ public class DatabaseUtils { } /** - * The legacy prefix matcher. - */ - private static String getSqlStatementPrefixSimple(@NonNull String sql) { - sql = sql.trim(); - if (sql.length() < 3) { - return null; - } - return sql.substring(0, 3).toUpperCase(Locale.ROOT); - } - - /** - * A regular expression that matches the first three characters in a SQL statement, after - * skipping past comments and whitespace. PREFIX_GROUP_NUM is the regex group that contains - * the matching prefix string. If PREFIX_REGEX is changed, PREFIX_GROUP_NUM may require an - * update too. - */ - private static final String PREFIX_REGEX = - "(" // Zero-or more... - + "\\s+" // Leading space - + "|" - + "--.*?\n" // Line comment - + "|" - + "/\\*[\\w\\W]*?\\*/" // Block comment - + ")*" - + "(\\w\\w\\w)"; // Three word-characters - private static final int PREFIX_GROUP_NUM = 2; - private static final Pattern sPrefixPattern = Pattern.compile(PREFIX_REGEX); - - /** - * Return the three-letter prefix of a SQL statement, skipping past whitespace and comments. - * Comments either start with "--" and run to the end of the line or are C-style block - * comments. The function returns null if a prefix could not be found. - */ - private static String getSqlStatementPrefixExtendedRegex(String sql) { - Matcher m = sPrefixPattern.matcher(sql); - if (m.lookingAt()) { - return m.group(PREFIX_GROUP_NUM).toUpperCase(Locale.ROOT); - } else { - return null; - } - } - - /** * Return the index of the first character past comments and whitespace. -1 is returned if * a comment is malformed. */ @@ -1719,15 +1674,7 @@ public class DatabaseUtils { * @hide */ public static int getSqlStatementTypeExtended(@NonNull String sql) { - if (Flags.simpleSqlCommentScanner()) { - return categorizeStatement(getSqlStatementPrefixExtendedNoRegex(sql), sql); - } else { - int type = categorizeStatement(getSqlStatementPrefixSimple(sql), sql); - if (type == STATEMENT_COMMENT) { - type = categorizeStatement(getSqlStatementPrefixExtendedRegex(sql), sql); - } - return type; - } + return categorizeStatement(getSqlStatementPrefixExtendedNoRegex(sql), sql); } /** diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 914aa51e5314..d77e6280d19c 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -129,9 +129,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen // Restrict this connection to read-only operations. private boolean mOnlyAllowReadOnlyOperations; - // Allow this connection to treat updates to temporary tables as read-only operations. - private boolean mAllowTempTableRetry = Flags.sqliteAllowTempTables(); - // The number of times attachCancellationSignal has been called. // Because SQLite statement execution can be reentrant, we keep track of how many // times we have attempted to attach a cancellation signal to the connection so that @@ -1281,19 +1278,17 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen /** * Verify that the statement is read-only, if the connection only allows read-only - * operations. If the connection allows updates to temporary tables, then the statement is - * read-only if the only updates are to temporary tables. + * operations. If the statement is not read-only, then check if the statement only modifies + * temp tables, in which case it is treated the same as a read-only statement and is allowed. * @param statement The statement to check. * @throws SQLiteException if the statement could update the database inside a read-only * transaction. */ void throwIfStatementForbidden(PreparedStatement statement) { if (mOnlyAllowReadOnlyOperations && !statement.mReadOnly) { - if (mAllowTempTableRetry) { - statement.mReadOnly = - nativeUpdatesTempOnly(mConnectionPtr, statement.mStatementPtr); - if (statement.mReadOnly) return; - } + statement.mReadOnly = + nativeUpdatesTempOnly(mConnectionPtr, statement.mStatementPtr); + if (statement.mReadOnly) return; throw new SQLiteException("Cannot execute this statement because it " + "might modify the database but the connection is read-only."); diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig index 285f984faab7..d5a7db82d456 100644 --- a/core/java/android/database/sqlite/flags.aconfig +++ b/core/java/android/database/sqlite/flags.aconfig @@ -9,19 +9,3 @@ flag { description: "SQLite APIs held back for Android 15" bug: "279043253" } - -flag { - name: "sqlite_allow_temp_tables" - namespace: "system_performance" - is_fixed_read_only: true - description: "Permit updates to TEMP tables in read-only transactions" - bug: "317993835" -} - -flag { - name: "simple_sql_comment_scanner" - namespace: "system_performance" - is_fixed_read_only: true - description: "Scan SQL comments by hand instead of with a regex" - bug: "329118560" -} diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig index 047d1fa4f49a..26ffa11823d8 100644 --- a/core/java/android/hardware/biometrics/flags.aconfig +++ b/core/java/android/hardware/biometrics/flags.aconfig @@ -39,3 +39,11 @@ flag { description: "This flag controls whether LSKF fallback is removed from biometric prompt when the phone is outside trusted locations" bug: "322081563" } + +flag { + name: "screen_off_unlock_udfps" + is_exported: true + namespace: "biometrics_integration" + description: "This flag controls Whether to enable fp unlock when screen turns off on udfps devices" + bug: "373792870" +} diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index 47541ca16cda..59a602ca092d 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -18,6 +18,7 @@ package android.hardware.display; import android.annotation.TestApi; import android.content.Context; +import android.hardware.biometrics.Flags; import android.os.Build; import android.os.SystemProperties; import android.provider.Settings; @@ -41,6 +42,7 @@ public class AmbientDisplayConfiguration { private final Context mContext; private final boolean mAlwaysOnByDefault; private final boolean mPickupGestureEnabledByDefault; + private final boolean mScreenOffUdfpsEnabledByDefault; /** Copied from android.provider.Settings.Secure since these keys are hidden. */ private static final String[] DOZE_SETTINGS = { @@ -68,6 +70,8 @@ public class AmbientDisplayConfiguration { mAlwaysOnByDefault = mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnEnabled); mPickupGestureEnabledByDefault = mContext.getResources().getBoolean(R.bool.config_dozePickupGestureEnabled); + mScreenOffUdfpsEnabledByDefault = + mContext.getResources().getBoolean(R.bool.config_screen_off_udfps_enabled); } /** @hide */ @@ -146,7 +150,9 @@ public class AmbientDisplayConfiguration { /** @hide */ public boolean screenOffUdfpsEnabled(int user) { return !TextUtils.isEmpty(udfpsLongPressSensorType()) - && boolSettingDefaultOff("screen_off_udfps_enabled", user); + && ((mScreenOffUdfpsEnabledByDefault && Flags.screenOffUnlockUdfps()) + ? boolSettingDefaultOn("screen_off_udfps_enabled", user) + : boolSettingDefaultOff("screen_off_udfps_enabled", user)); } /** @hide */ diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java index 5ee61bcd436a..2df541818e3d 100644 --- a/core/java/android/hardware/input/KeyGestureEvent.java +++ b/core/java/android/hardware/input/KeyGestureEvent.java @@ -99,6 +99,7 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT = 59; public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT = 60; public static final int KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS = 61; + public static final int KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY = 62; public static final int FLAG_CANCELLED = 1; @@ -175,7 +176,7 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT, KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, - + KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { @@ -415,6 +416,8 @@ public final class KeyGestureEvent { case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT: case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT: return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS; + case KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MOVE_TO_NEXT_DISPLAY; case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT: return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT; case KEY_GESTURE_TYPE_LOCK_SCREEN: @@ -530,6 +533,8 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT"; case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT: return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT"; + case KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY: + return "KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY"; case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT: return "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT"; case KEY_GESTURE_TYPE_LOCK_SCREEN: diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java index 22ae67672950..2ba107805569 100644 --- a/core/java/android/hardware/soundtrigger/ConversionUtil.java +++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java @@ -40,6 +40,7 @@ import android.os.SharedMemory; import android.system.ErrnoException; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Locale; import java.util.UUID; @@ -170,17 +171,18 @@ public class ConversionUtil { public static SoundTrigger.RecognitionConfig aidl2apiRecognitionConfig( RecognitionConfig aidlConfig) { - var keyphrases = - new SoundTrigger.KeyphraseRecognitionExtra[aidlConfig.phraseRecognitionExtras.length]; - int i = 0; + var keyphrases = new ArrayList<SoundTrigger.KeyphraseRecognitionExtra>( + aidlConfig.phraseRecognitionExtras.length); for (var extras : aidlConfig.phraseRecognitionExtras) { - keyphrases[i++] = aidl2apiPhraseRecognitionExtra(extras); + keyphrases.add(aidl2apiPhraseRecognitionExtra(extras)); } - return new SoundTrigger.RecognitionConfig(aidlConfig.captureRequested, - false /** allowMultipleTriggers **/, - keyphrases, - Arrays.copyOf(aidlConfig.data, aidlConfig.data.length), - aidl2apiAudioCapabilities(aidlConfig.audioCapabilities)); + return new SoundTrigger.RecognitionConfig.Builder() + .setCaptureRequested(aidlConfig.captureRequested) + .setAllowMultipleTriggers(false) + .setKeyphrases(keyphrases) + .setData(Arrays.copyOf(aidlConfig.data, aidlConfig.data.length)) + .setAudioCapabilities(aidl2apiAudioCapabilities(aidlConfig.audioCapabilities)) + .build(); } public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra( diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index 05e91e447a43..a1e7567faead 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -1529,8 +1529,6 @@ public class SoundTrigger { * config that can be used by * {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} * - * @deprecated should use builder-based constructor instead. - * TODO(b/368042125): remove this method. * @param captureRequested Whether the DSP should capture the trigger sound. * @param allowMultipleTriggers Whether the service should restart listening after the DSP * triggers. @@ -1538,15 +1536,10 @@ public class SoundTrigger { * @param data Opaque data for use by system applications who know about voice engine * internals, typically during enrollment. * @param audioCapabilities Bit field encoding of the AudioCapabilities. - * - * @hide */ - @Deprecated - @SuppressWarnings("Todo") - @TestApi - public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, - @SuppressLint("ArrayReturn") @Nullable KeyphraseRecognitionExtra[] keyphrases, - @Nullable byte[] data, int audioCapabilities) { + private RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, + @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data, + int audioCapabilities) { this.mCaptureRequested = captureRequested; this.mAllowMultipleTriggers = allowMultipleTriggers; this.mKeyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0]; @@ -1558,6 +1551,7 @@ public class SoundTrigger { * Constructor for {@link RecognitionConfig} without audioCapabilities. The * audioCapabilities is set to 0. * + * @deprecated Use {@link Builder} instead. * @param captureRequested Whether the DSP should capture the trigger sound. * @param allowMultipleTriggers Whether the service should restart listening after the DSP * triggers. @@ -1567,10 +1561,10 @@ public class SoundTrigger { * @hide */ @UnsupportedAppUsage + @Deprecated @TestApi public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, - @SuppressLint("ArrayReturn") @Nullable KeyphraseRecognitionExtra[] keyphrases, - @Nullable byte[] data) { + @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) { this(captureRequested, allowMultipleTriggers, keyphrases, data, 0); } @@ -1718,7 +1712,7 @@ public class SoundTrigger { /** * Sets capture requested state. - * @param captureRequested The new requested state. + * @param captureRequested Whether the DSP should capture the trigger sound. * @return the same Builder instance. */ public @NonNull Builder setCaptureRequested(boolean captureRequested) { @@ -1728,7 +1722,8 @@ public class SoundTrigger { /** * Sets allow multiple triggers state. - * @param allowMultipleTriggers The new allow multiple triggers state. + * @param allowMultipleTriggers Whether the service should restart listening after the + * DSP triggers. * @return the same Builder instance. */ public @NonNull Builder setAllowMultipleTriggers(boolean allowMultipleTriggers) { @@ -1738,7 +1733,8 @@ public class SoundTrigger { /** * Sets the keyphrases field. - * @param keyphrases The new keyphrases. + * @param keyphrases The list of keyphrase specific data associated with this + * recognition session. * @return the same Builder instance. */ public @NonNull Builder setKeyphrases( @@ -1749,7 +1745,9 @@ public class SoundTrigger { /** * Sets the data field. - * @param data The new data. + * @param data Opaque data provided to the DSP associated with this recognition session, + * which is used by system applications who know about voice engine + * internals, typically during enrollment. * @return the same Builder instance. */ public @NonNull Builder setData(@Nullable byte[] data) { @@ -1759,7 +1757,8 @@ public class SoundTrigger { /** * Sets the audio capabilities field. - * @param audioCapabilities The new audio capabilities. + * @param audioCapabilities The bit field encoding of the audio capabilities associated + * with this recognition session. * @return the same Builder instance. */ public @NonNull Builder setAudioCapabilities(int audioCapabilities) { diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java index 46cf0163c0e5..c0398ce1fcf1 100644 --- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java @@ -40,9 +40,9 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.vcn.util.PersistableBundleUtils; diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 1c9be6fb4b82..f275714e2cf5 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -28,13 +28,13 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; -import android.os.Binder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceSpecificException; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.net.module.util.BinderUtils; import java.io.IOException; import java.lang.annotation.Retention; @@ -711,7 +711,7 @@ public class VcnManager { @Override public void onPolicyChanged() { - Binder.withCleanCallingIdentity( + BinderUtils.withCleanCallingIdentity( () -> mExecutor.execute(() -> mListener.onPolicyChanged())); } } @@ -734,7 +734,7 @@ public class VcnManager { @Override public void onVcnStatusChanged(@VcnStatusCode int statusCode) { - Binder.withCleanCallingIdentity( + BinderUtils.withCleanCallingIdentity( () -> mExecutor.execute(() -> mCallback.onStatusChanged(statusCode))); } @@ -747,7 +747,7 @@ public class VcnManager { @Nullable String exceptionMessage) { final Throwable cause = createThrowableByClassName(exceptionClass, exceptionMessage); - Binder.withCleanCallingIdentity( + BinderUtils.withCleanCallingIdentity( () -> mExecutor.execute( () -> diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java index edf2c093bc8b..16114dd135af 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java @@ -21,10 +21,10 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.PersistableBundle; +import android.util.IndentingPrintWriter; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java index 2e6b09f032fb..c7b2f188ba96 100644 --- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java @@ -29,9 +29,9 @@ import android.net.NetworkCapabilities; import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria; import android.os.PersistableBundle; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.util.PersistableBundleUtils; import java.util.ArrayList; diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index ddcb5c60ab80..6c3c2852c7e7 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -24,6 +24,8 @@ import android.util.Range; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BatteryStatsHistory; import com.android.internal.os.BatteryStatsHistoryIterator; import com.android.internal.os.MonotonicClock; @@ -43,7 +45,9 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis. @@ -126,6 +130,12 @@ public final class BatteryUsageStats implements Parcelable, Closeable { // Max window size. CursorWindow uses only as much memory as needed. private static final long BATTERY_CONSUMER_CURSOR_WINDOW_SIZE = 20_000_000; // bytes + /** + * Used by tests to ensure all BatteryUsageStats instances are closed. + */ + @VisibleForTesting + public static boolean DEBUG_INSTANCE_COUNT; + private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000; private static final int[] UID_USAGE_TIME_PROCESS_STATES = { @@ -153,7 +163,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private final List<UserBatteryConsumer> mUserBatteryConsumers; private final AggregateBatteryConsumer[] mAggregateBatteryConsumers; private final BatteryStatsHistory mBatteryStatsHistory; - private BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout; + private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout; private CursorWindow mBatteryConsumersCursorWindow; private BatteryUsageStats(@NonNull Builder builder) { @@ -873,6 +883,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { @Override public void close() throws IOException { + onCursorWindowReleased(mBatteryConsumersCursorWindow); mBatteryConsumersCursorWindow.close(); mBatteryConsumersCursorWindow = null; } @@ -880,6 +891,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { @Override protected void finalize() throws Throwable { if (mBatteryConsumersCursorWindow != null) { + // Do not decrement sOpenCusorWindowCount. All instances should be closed explicitly mBatteryConsumersCursorWindow.close(); } super.finalize(); @@ -934,6 +946,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { boolean includesPowerStateData, double minConsumedPowerThreshold) { mBatteryConsumersCursorWindow = new CursorWindow(null, BATTERY_CONSUMER_CURSOR_WINDOW_SIZE); + onCursorWindowAllocated(mBatteryConsumersCursorWindow); mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout( customPowerComponentNames, includePowerModels, includeProcessStateData, includeScreenStateData, includesPowerStateData); @@ -996,6 +1009,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { */ public void discard() { mBatteryConsumersCursorWindow.close(); + onCursorWindowReleased(mBatteryConsumersCursorWindow); } /** @@ -1264,4 +1278,50 @@ public final class BatteryUsageStats implements Parcelable, Closeable { } } } + + @GuardedBy("BatteryUsageStats.class") + private static Map<CursorWindow, Exception> sInstances; + + private static void onCursorWindowAllocated(CursorWindow window) { + if (!DEBUG_INSTANCE_COUNT) { + return; + } + + synchronized (BatteryUsageStats.class) { + if (sInstances == null) { + sInstances = new HashMap<>(); + } + sInstances.put(window, new Exception()); + } + } + + private static void onCursorWindowReleased(CursorWindow window) { + if (!DEBUG_INSTANCE_COUNT) { + return; + } + + synchronized (BatteryUsageStats.class) { + sInstances.remove(window); + } + } + + /** + * Used by tests to ensure all BatteryUsageStats instances are closed. + */ + @VisibleForTesting + public static void assertAllInstancesClosed() { + if (!DEBUG_INSTANCE_COUNT) { + throw new IllegalStateException("DEBUG_INSTANCE_COUNT is false"); + } + + synchronized (BatteryUsageStats.class) { + if (!sInstances.isEmpty()) { + Exception callSite = sInstances.entrySet().iterator().next().getValue(); + int count = sInstances.size(); + sInstances.clear(); + throw new IllegalStateException( + "Instances of BatteryUsageStats not closed: " + count, callSite); + } + } + } } diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java index 9419032c46f8..9dec8673f019 100644 --- a/core/java/android/os/VibratorInfo.java +++ b/core/java/android/os/VibratorInfo.java @@ -316,9 +316,7 @@ public class VibratorInfo implements Parcelable { * @return True if the hardware can control the frequency of the vibrations, otherwise false. */ public boolean hasFrequencyControl() { - // We currently can only control frequency of the vibration using the compose PWLE method. - return hasCapability( - IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS); + return hasCapability(IVibrator.CAP_FREQUENCY_CONTROL); } /** @@ -481,7 +479,8 @@ public class VibratorInfo implements Parcelable { * @return True if the hardware supports creating envelope effects, false otherwise. */ public boolean areEnvelopeEffectsSupported() { - return hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2); + return hasCapability( + IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0be55844b829..764570ead4e1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -19305,14 +19305,14 @@ public final class Settings { * If hotword detection should be enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String HOTWORD_DETECTION_ENABLED = "hotword_detection_enabled"; /** * Whether Smart Replies are enabled within Wear. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SMART_REPLIES_ENABLED = "smart_replies_enabled"; /** @@ -19326,7 +19326,7 @@ public final class Settings { * If FLP should obtain location data from the paired device. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String OBTAIN_PAIRED_DEVICE_LOCATION = "obtain_paired_device_location"; @@ -19334,7 +19334,7 @@ public final class Settings { * The play store availability on companion phone. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String PHONE_PLAY_STORE_AVAILABILITY = "phone_play_store_availability"; @@ -19350,7 +19350,7 @@ public final class Settings { * Whether the bug report is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String BUG_REPORT = "bug_report"; // Possible bug report states @@ -19363,14 +19363,14 @@ public final class Settings { * The enabled/disabled state of the SmartIlluminate. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SMART_ILLUMINATE_ENABLED = "smart_illuminate_enabled"; /** * Whether automatic time is enabled on the watch. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CLOCKWORK_AUTO_TIME = "clockwork_auto_time"; // Possible clockwork auto time states @@ -19388,7 +19388,7 @@ public final class Settings { * Whether automatic time zone is enabled on the watch. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CLOCKWORK_AUTO_TIME_ZONE = "clockwork_auto_time_zone"; // Possible clockwork auto time zone states @@ -19405,14 +19405,14 @@ public final class Settings { * Whether 24 hour time format is enabled on the watch. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CLOCKWORK_24HR_TIME = "clockwork_24hr_time"; /** * Whether the auto wifi toggle setting is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AUTO_WIFI = "auto_wifi"; // Possible force wifi on states @@ -19432,7 +19432,7 @@ public final class Settings { * wifi requirement until this time). The time is in millis since epoch. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS = "alt_bypass_wifi_requirement_time_millis"; @@ -19440,7 +19440,7 @@ public final class Settings { * Whether the setup was skipped. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SETUP_SKIPPED = "setup_skipped"; // Possible setup_skipped states @@ -19455,7 +19455,7 @@ public final class Settings { * The last requested call forwarding action. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String LAST_CALL_FORWARD_ACTION = "last_call_forward_action"; // Possible call forwarding actions @@ -19468,31 +19468,31 @@ public final class Settings { // Stem button settings. /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_1_TYPE = "STEM_1_TYPE"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_1_DATA = "STEM_1_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_1_DEFAULT_DATA = "STEM_1_DEFAULT_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_2_TYPE = "STEM_2_TYPE"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_2_DATA = "STEM_2_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_2_DEFAULT_DATA = "STEM_2_DEFAULT_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_3_TYPE = "STEM_3_TYPE"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_3_DATA = "STEM_3_DATA"; /** @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String STEM_3_DEFAULT_DATA = "STEM_3_DEFAULT_DATA"; // Stem types @@ -19507,14 +19507,14 @@ public final class Settings { * If the device should be muted when off body. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String MUTE_WHEN_OFF_BODY_ENABLED = "obtain_mute_when_off_body"; /** * Wear OS version string. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WEAR_OS_VERSION_STRING = "wear_os_version_string"; /** @@ -19527,28 +19527,28 @@ public final class Settings { * The android wear system version. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String ANDROID_WEAR_VERSION = "android_wear_version"; /** * The wear system capabiltiies. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SYSTEM_CAPABILITIES = "system_capabilities"; /** * The android wear system edition. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SYSTEM_EDITION = "android_wear_system_edition"; /** * The Wear platform MR number. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number"; /** @@ -19562,42 +19562,42 @@ public final class Settings { * Whether ambient is currently enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_ENABLED = "ambient_enabled"; /** * Whether ambient tilt to wake is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_TILT_TO_WAKE = "ambient_tilt_to_wake"; /** * Whether ambient low bit mode is enabled by developer options. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_LOW_BIT_ENABLED_DEV = "ambient_low_bit_enabled_dev"; /** * Whether ambient touch to wake is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_TOUCH_TO_WAKE = "ambient_touch_to_wake"; /** * Whether ambient tilt to bright is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_TILT_TO_BRIGHT = "ambient_tilt_to_bright"; /** * Whether touch and hold to edit WF is enabled * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String GESTURE_TOUCH_AND_HOLD_WATCH_FACE_ENABLED = "gesture_touch_and_hold_watchface_enabled"; @@ -19611,7 +19611,7 @@ public final class Settings { * Whether bedtime mode is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String BEDTIME_MODE = "bedtime_mode"; /** @@ -19623,35 +19623,35 @@ public final class Settings { * Whether the current watchface is decomposable. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String DECOMPOSABLE_WATCHFACE = "current_watchface_decomposable"; /** * Whether to force ambient when docked. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_FORCE_WHEN_DOCKED = "ambient_force_when_docked"; /** * Whether the ambient low bit mode is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_LOW_BIT_ENABLED = "ambient_low_bit_enabled"; /** * The timeout duration in minutes of ambient mode when plugged in. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String AMBIENT_PLUGGED_TIMEOUT_MIN = "ambient_plugged_timeout_min"; /** * What OS does paired device has. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String PAIRED_DEVICE_OS_TYPE = "paired_device_os_type"; // Possible values of PAIRED_DEVICE_OS_TYPE @@ -19686,7 +19686,7 @@ public final class Settings { * The user's last setting for hfp client. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String USER_HFP_CLIENT_SETTING = "user_hfp_client_setting"; // Possible hfp client user setting values @@ -19711,7 +19711,7 @@ public final class Settings { * The companion App name. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String COMPANION_APP_NAME = "wear_companion_app_name"; /** @@ -19719,21 +19719,21 @@ public final class Settings { * wear. 1 for supporting, 0 for not supporting. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String ENABLE_ALL_LANGUAGES = "enable_all_languages"; /** * The Locale (as language tag) the user chose at startup. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String SETUP_LOCALE = "setup_locale"; /** * The version of oem setup present. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String OEM_SETUP_VERSION = "oem_setup_version"; /** @@ -19779,7 +19779,7 @@ public final class Settings { * -{@link BATTERY_SAVER_MODE_CUSTOM} * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String BATTERY_SAVER_MODE = "battery_saver_mode"; /** @@ -19836,7 +19836,7 @@ public final class Settings { * If burn in protection is enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String BURN_IN_PROTECTION_ENABLED = "burn_in_protection"; /** @@ -19855,7 +19855,7 @@ public final class Settings { * RIGHT_WRIST_ROTATION_0 = "2", RIGHT_WRIST_ROTATION_180 = "3" * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WRIST_ORIENTATION_MODE = "wear_wrist_orientation_mode"; /** @@ -19894,7 +19894,7 @@ public final class Settings { * * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CLOCKWORK_SYSUI_PACKAGE = "clockwork_sysui_package"; /** @@ -19924,7 +19924,7 @@ public final class Settings { * Whether the device has Wet Mode/ Touch Lock Mode enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WET_MODE_ON = "wet_mode_on"; /** @@ -19943,7 +19943,7 @@ public final class Settings { * Whether charging sounds are enabled. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CHARGING_SOUNDS_ENABLED = "wear_charging_sounds_enabled"; /** @@ -19952,7 +19952,7 @@ public final class Settings { * * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String DYNAMIC_COLOR_THEME_ENABLED = "dynamic_color_theme_enabled"; /** @@ -20044,7 +20044,7 @@ public final class Settings { * The key to indicate the data migration status on device upgrade in Wear Services. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String UPGRADE_DATA_MIGRATION_STATUS = "upgrade_data_migration_status"; @@ -20101,20 +20101,20 @@ public final class Settings { * The custom foreground color. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CUSTOM_COLOR_FOREGROUND = "custom_foreground_color"; /** * The custom background color. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String CUSTOM_COLOR_BACKGROUND = "custom_background_color"; /** The status of the phone switching process. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String PHONE_SWITCHING_STATUS = "phone_switching_status"; /** @@ -20291,7 +20291,7 @@ public final class Settings { * Controls the launcher ui mode on wearable devices. * @hide */ - @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Readable public static final String WEAR_LAUNCHER_UI_MODE = "wear_launcher_ui_mode"; /** Whether Wear Power Anomaly Service is enabled. diff --git a/core/java/android/security/forensic/ForensicEvent.aidl b/core/java/android/security/forensic/ForensicEvent.aidl new file mode 100644 index 000000000000..a321fb0cb939 --- /dev/null +++ b/core/java/android/security/forensic/ForensicEvent.aidl @@ -0,0 +1,20 @@ +/* + * 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 android.security.forensic; + +/** {@hide} */ +parcelable ForensicEvent; diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/forensic/ForensicEvent.java new file mode 100644 index 000000000000..9cbc5ecea962 --- /dev/null +++ b/core/java/android/security/forensic/ForensicEvent.java @@ -0,0 +1,84 @@ +/* + * 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 android.security.forensic; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.security.Flags; +import android.util.ArrayMap; + +import java.util.Map; + +/** + * A class that represents a forensic event. + * @hide + */ +@FlaggedApi(Flags.FLAG_AFL_API) +public final class ForensicEvent implements Parcelable { + private static final String TAG = "ForensicEvent"; + + @NonNull + private final String mType; + + @NonNull + private final Map<String, String> mKeyValuePairs; + + public static final @NonNull Parcelable.Creator<ForensicEvent> CREATOR = + new Parcelable.Creator<>() { + public ForensicEvent createFromParcel(Parcel in) { + return new ForensicEvent(in); + } + + public ForensicEvent[] newArray(int size) { + return new ForensicEvent[size]; + } + }; + + public ForensicEvent(@NonNull String type, @NonNull Map<String, String> keyValuePairs) { + mType = type; + mKeyValuePairs = keyValuePairs; + } + + private ForensicEvent(@NonNull Parcel in) { + mType = in.readString(); + mKeyValuePairs = new ArrayMap<>(in.readInt()); + in.readMap(mKeyValuePairs, getClass().getClassLoader(), String.class, String.class); + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeString(mType); + out.writeInt(mKeyValuePairs.size()); + out.writeMap(mKeyValuePairs); + } + + @FlaggedApi(Flags.FLAG_AFL_API) + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "ForensicEvent{" + + "mType=" + mType + + ", mKeyValuePairs=" + mKeyValuePairs + + '}'; + } +} diff --git a/core/java/android/security/forensic/IBackupTransport.aidl b/core/java/android/security/forensic/IBackupTransport.aidl new file mode 100644 index 000000000000..c2cbc83ba1b3 --- /dev/null +++ b/core/java/android/security/forensic/IBackupTransport.aidl @@ -0,0 +1,41 @@ +/* + * 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 android.security.forensic; +import android.security.forensic.ForensicEvent; + +import com.android.internal.infra.AndroidFuture; + +/** {@hide} */ +oneway interface IBackupTransport { + /** + * Initialize the server side. + */ + void initialize(in AndroidFuture<int> resultFuture); + + /** + * Send forensic logging data to the backup destination. + * The data is a list of ForensicEvent. + * The ForensicEvent is an abstract class that represents + * different type of events. + */ + void addData(in List<ForensicEvent> events, in AndroidFuture<int> resultFuture); + + /** + * Release the binder to the server. + */ + void release(in AndroidFuture<int> resultFuture); +} diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig index b593902a95d4..9bb1039849e5 100644 --- a/core/java/android/security/responsible_apis_flags.aconfig +++ b/core/java/android/security/responsible_apis_flags.aconfig @@ -77,4 +77,11 @@ flag { description: "Prevent intent redirect attacks" bug: "361143368" is_fixed_read_only: true +} + +flag { + name: "prevent_intent_redirect_abort_or_throw_exception" + namespace: "responsible_apis" + description: "Prevent intent redirect attacks by aborting or throwing security exception" + bug: "361143368" }
\ No newline at end of file diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index ccc17ecccbf9..2e660fc1f157 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -73,6 +73,7 @@ import com.android.internal.infra.AndroidFuture; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -1513,10 +1514,11 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { "Recognition for the given keyphrase is not supported"); } - KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1]; + List<KeyphraseRecognitionExtra> recognitionExtra = + new ArrayList<KeyphraseRecognitionExtra>(1); // TODO: Do we need to do something about the confidence level here? - recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.getId(), - mKeyphraseMetadata.getRecognitionModeFlags(), 0, new ConfidenceLevel[0]); + recognitionExtra.add(new KeyphraseRecognitionExtra(mKeyphraseMetadata.getId(), + mKeyphraseMetadata.getRecognitionModeFlags(), 0, new ConfidenceLevel[0])); boolean captureTriggerAudio = (recognitionFlags&RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0; boolean allowMultipleTriggers = @@ -1534,10 +1536,17 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { int code; try { code = mSoundTriggerSession.startRecognition( - mKeyphraseMetadata.getId(), mLocale.toLanguageTag(), mInternalCallback, - new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers, - recognitionExtra, data, audioCapabilities), - runInBatterySaver); + mKeyphraseMetadata.getId(), + mLocale.toLanguageTag(), + mInternalCallback, + new RecognitionConfig.Builder() + .setCaptureRequested(captureTriggerAudio) + .setAllowMultipleTriggers(allowMultipleTriggers) + .setKeyphrases(recognitionExtra) + .setData(data) + .setAudioCapabilities(audioCapabilities) + .build(), + runInBatterySaver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index e8ef9d65a2b4..bce51f297aff 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -1701,6 +1701,11 @@ public class PhoneStateListener { public final void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) { // not supported on the deprecated interface - Use TelephonyCallback instead } + + public final void onCarrierRoamingNtnAvailableServicesChanged( + @NetworkRegistrationInfo.ServiceType int[] availableServices) { + // not supported on the deprecated interface - Use TelephonyCallback instead + } } private void log(String s) { diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index 5295b606dd19..46e27dc60adc 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -681,6 +681,20 @@ public class TelephonyCallback { public static final int EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED = 43; /** + * Event for listening to changes in carrier roaming non-terrestrial network available services + * via callback onCarrierRoamingNtnAvailableServicesChanged(). + * This callback is triggered when the available services provided by the carrier roaming + * satellite changes. The carrier roaming satellite is defined by the following conditions. + * <ul> + * <li>Subscription supports attaching to satellite which is defined by + * {@link CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} </li> + * </ul> + * + * @hide + */ + public static final int EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED = 44; + + /** * @hide */ @IntDef(prefix = {"EVENT_"}, value = { @@ -726,7 +740,8 @@ public class TelephonyCallback { EVENT_EMERGENCY_CALLBACK_MODE_CHANGED, EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED, EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED, - EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED + EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED, + EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED }) @Retention(RetentionPolicy.SOURCE) public @interface TelephonyEvent { @@ -1784,6 +1799,15 @@ public class TelephonyCallback { * </ul> */ default void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) {} + + /** + * Callback invoked when carrier roaming non-terrestrial network available + * service changes. + * + * @param availableServices The list of the supported services. + */ + default void onCarrierRoamingNtnAvailableServicesChanged( + @NetworkRegistrationInfo.ServiceType List<Integer> availableServices) {} } /** @@ -2235,5 +2259,19 @@ public class TelephonyCallback { Binder.withCleanCallingIdentity(() -> mExecutor.execute( () -> listener.onCarrierRoamingNtnEligibleStateChanged(eligible))); } + + public void onCarrierRoamingNtnAvailableServicesChanged( + @NetworkRegistrationInfo.ServiceType int[] availableServices) { + if (!Flags.carrierRoamingNbIotNtn()) return; + + CarrierRoamingNtnModeListener listener = + (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + List<Integer> ServiceList = Arrays.stream(availableServices).boxed() + .collect(Collectors.toList()); + Binder.withCleanCallingIdentity(() -> mExecutor.execute( + () -> listener.onCarrierRoamingNtnAvailableServicesChanged(ServiceList))); + } } } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 3c7e924f07df..4d50a450490e 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -1118,6 +1118,21 @@ public class TelephonyRegistryManager { } /** + * Notify external listeners that carrier roaming non-terrestrial available services changed. + * @param availableServices The list of the supported services. + * @hide + */ + public void notifyCarrierRoamingNtnAvailableServicesChanged( + int subId, @NetworkRegistrationInfo.ServiceType int[] availableServices) { + try { + sRegistry.notifyCarrierRoamingNtnAvailableServicesChanged(subId, availableServices); + } catch (RemoteException ex) { + // system server crash + throw ex.rethrowFromSystemServer(); + } + } + + /** * Processes potential event changes from the provided {@link TelephonyCallback}. * * @param telephonyCallback callback for monitoring callback changes to the telephony state. @@ -1272,12 +1287,9 @@ public class TelephonyRegistryManager { if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnModeListener) { eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED); - } - - if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnModeListener) { eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED); } - return eventList; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0efded2d0eb9..d57a88075f8a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -131,6 +131,7 @@ import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_ import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme; import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay; +import static com.android.window.flags.Flags.predictiveBackSwipeEdgeNoneApi; import static com.android.window.flags.Flags.setScPropertiesInClient; import static com.android.window.flags.Flags.systemUiImmersiveConfirmationDialog; @@ -7559,8 +7560,13 @@ public final class ViewRootImpl implements ViewParent, // - 0 means the button was pressed. // - 1 means the button continues to be pressed (long press). if (keyEvent.getRepeatCount() == 0) { - animationCallback.onBackStarted( - new BackEvent(0, 0, 0f, BackEvent.EDGE_LEFT)); + BackEvent backEvent; + if (predictiveBackSwipeEdgeNoneApi()) { + backEvent = new BackEvent(0, 0, 0f, BackEvent.EDGE_NONE); + } else { + backEvent = new BackEvent(0, 0, 0f, BackEvent.EDGE_LEFT); + } + animationCallback.onBackStarted(backEvent); } break; case KeyEvent.ACTION_UP: diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java index 23e572fcd577..90fac361c966 100644 --- a/core/java/android/window/BackEvent.java +++ b/core/java/android/window/BackEvent.java @@ -16,6 +16,7 @@ package android.window; +import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_SWIPE_EDGE_NONE_API; import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API; import static com.android.window.flags.Flags.predictiveBackTimestampApi; @@ -37,11 +38,19 @@ public final class BackEvent { public static final int EDGE_LEFT = 0; /** Indicates that the edge swipe starts from the right edge of the screen */ public static final int EDGE_RIGHT = 1; + /** + * Indicates that the back event was not triggered by an edge swipe back gesture. This applies + * to cases like using the back button in 3-button navigation or pressing a hardware back + * button. + */ + @FlaggedApi(FLAG_PREDICTIVE_BACK_SWIPE_EDGE_NONE_API) + public static final int EDGE_NONE = 2; /** @hide */ @IntDef({ EDGE_LEFT, EDGE_RIGHT, + EDGE_NONE, }) @Retention(RetentionPolicy.SOURCE) public @interface SwipeEdge{} diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 0bcc9c1f58c5..a0d58d5a0c76 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -348,3 +348,10 @@ flag { description: "Enables a switch to change the concequence of dragging a window to the top edge." bug: "372614715" } + +flag { + name: "enable_task_resizing_keyboard_shortcuts" + namespace: "lse_desktop_experience" + description: "Enables keyboard shortcuts for resizing tasks in desktop mode." + bug: "335819608" +} diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index d15f52c39efa..966e835018f3 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -332,3 +332,11 @@ flag { is_fixed_read_only: true bug: "362938401" } + +flag { + name: "predictive_back_swipe_edge_none_api" + namespace: "systemui" + description: "EDGE_NONE swipeEdge option in BackEvent" + is_fixed_read_only: true + bug: "362938401" +} diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index 6448f10f01b9..003393c337e7 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -230,7 +230,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai mRendererWrapper = mSurfaceOnly ? null : renderer; mMetricsWrapper = mSurfaceOnly ? null : metrics; mViewRoot = mSurfaceOnly ? null : viewRootWrapper; - mObserver = mSurfaceOnly + mObserver = mSurfaceOnly || (Flags.useSfFrameDuration() && Flags.ignoreHwuiIsFirstFrame()) ? null : new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), mHandler, /* waitForPresentTime= */ false); @@ -253,6 +253,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() { @Override public void surfaceCreated(SurfaceControl.Transaction t) { + Trace.beginSection("FrameTracker#surfaceCreated"); mHandler.runWithScissors(() -> { if (mSurfaceControl == null) { mSurfaceControl = mViewRoot.getSurfaceControl(); @@ -262,6 +263,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } } }, EXECUTOR_TASK_TIMEOUT); + Trace.endSection(); } @Override @@ -464,23 +466,28 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai @Override public void onJankDataAvailable(SurfaceControl.JankData[] jankData) { postCallback(() -> { - if (mCancelled || mMetricsFinalized) { - return; - } - - for (SurfaceControl.JankData jankStat : jankData) { - if (!isInRange(jankStat.frameVsyncId)) { - continue; + try { + Trace.beginSection("FrameTracker#onJankDataAvailable"); + if (mCancelled || mMetricsFinalized) { + return; } - JankInfo info = findJankInfo(jankStat.frameVsyncId); - if (info != null) { - info.update(jankStat); - } else { - mJankInfos.put((int) jankStat.frameVsyncId, - JankInfo.createFromSurfaceControlCallback(jankStat)); + + for (SurfaceControl.JankData jankStat : jankData) { + if (!isInRange(jankStat.frameVsyncId)) { + continue; + } + JankInfo info = findJankInfo(jankStat.frameVsyncId); + if (info != null) { + info.update(jankStat); + } else { + mJankInfos.put((int) jankStat.frameVsyncId, + JankInfo.createFromSurfaceControlCallback(jankStat)); + } } + processJankInfos(); + } finally { + Trace.endSection(); } - processJankInfos(); }); } @@ -507,29 +514,35 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai @Override public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { postCallback(() -> { - if (mCancelled || mMetricsFinalized) { - return; - } - - // Since this callback might come a little bit late after the end() call. - // We should keep tracking the begin / end timestamp that we can compare with - // vsync timestamp to check if the frame is in the duration of the CUJ. - long totalDurationNanos = mMetricsWrapper.getMetric(FrameMetrics.TOTAL_DURATION); - boolean isFirstFrame = mMetricsWrapper.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1; - long frameVsyncId = - mMetricsWrapper.getTiming()[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID]; + try { + Trace.beginSection("FrameTracker#onFrameMetricsAvailable"); + if (mCancelled || mMetricsFinalized) { + return; + } - if (!isInRange(frameVsyncId)) { - return; - } - JankInfo info = findJankInfo(frameVsyncId); - if (info != null) { - info.update(totalDurationNanos, isFirstFrame); - } else { - mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback( - frameVsyncId, totalDurationNanos, isFirstFrame)); + // Since this callback might come a little bit late after the end() call. + // We should keep tracking the begin / end timestamp that we can compare with + // vsync timestamp to check if the frame is in the duration of the CUJ. + long totalDurationNanos = mMetricsWrapper.getMetric(FrameMetrics.TOTAL_DURATION); + boolean isFirstFrame = + mMetricsWrapper.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1; + long frameVsyncId = + mMetricsWrapper.getTiming()[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID]; + + if (!isInRange(frameVsyncId)) { + return; + } + JankInfo info = findJankInfo(frameVsyncId); + if (info != null) { + info.update(totalDurationNanos, isFirstFrame); + } else { + mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback( + frameVsyncId, totalDurationNanos, isFirstFrame)); + } + processJankInfos(); + } finally { + Trace.endSection(); } - processJankInfos(); }); } @@ -568,13 +581,20 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } private boolean callbacksReceived(JankInfo info) { - return mSurfaceOnly + return mObserver == null ? info.surfaceControlCallbackFired : info.hwuiCallbackFired && info.surfaceControlCallbackFired; } @UiThread private void finish() { + Trace.beginSection("FrameTracker#finish"); + finishTraced(); + Trace.endSection(); + } + + @UiThread + private void finishTraced() { if (mMetricsFinalized || mCancelled) return; mMetricsFinalized = true; @@ -599,7 +619,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai for (int i = 0; i < mJankInfos.size(); i++) { JankInfo info = mJankInfos.valueAt(i); final boolean isFirstDrawn = !mSurfaceOnly && info.isFirstFrame; - if (isFirstDrawn) { + if (isFirstDrawn && !Flags.ignoreHwuiIsFirstFrame()) { continue; } if (info.frameVsyncId > mEndVsyncId) { @@ -636,7 +656,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } // TODO (b/174755489): Early latch currently gets fired way too often, so we have // to ignore it for now. - if (!mSurfaceOnly && !info.hwuiCallbackFired) { + if (mObserver != null && !info.hwuiCallbackFired) { markEvent("FT#MissedHWUICallback", info.frameVsyncId); Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId + ", CUJ=" + name); @@ -762,7 +782,9 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai * @param observer observer */ public void addObserver(HardwareRendererObserver observer) { - mRenderer.addObserver(observer); + if (observer != null) { + mRenderer.addObserver(observer); + } } /** @@ -770,7 +792,9 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai * @param observer observer */ public void removeObserver(HardwareRendererObserver observer) { - mRenderer.removeObserver(observer); + if (observer != null) { + mRenderer.removeObserver(observer); + } } } diff --git a/core/java/com/android/internal/jank/flags.aconfig b/core/java/com/android/internal/jank/flags.aconfig index 82f50ae848b3..287ad1856258 100644 --- a/core/java/com/android/internal/jank/flags.aconfig +++ b/core/java/com/android/internal/jank/flags.aconfig @@ -8,3 +8,10 @@ flag { bug: "354763298" is_fixed_read_only: true } +flag { + name: "ignore_hwui_is_first_frame" + namespace: "window_surfaces" + description: "Whether to remove the usage of the HWUI 'is first frame' flag to ignore jank" + bug: "354763298" + is_fixed_read_only: true +} diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 07aa7206c0e7..b56aadd366d5 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -1770,6 +1770,10 @@ public class BatteryStatsHistory { @GuardedBy("this") private void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { + if (cur.eventCode != HistoryItem.EVENT_NONE && cur.eventTag.string == null) { + Slog.wtfStack(TAG, "Event " + Integer.toHexString(cur.eventCode) + " without a name"); + } + if (mTracer != null && mTracer.tracingEnabled()) { recordTraceEvents(cur.eventCode, cur.eventTag); recordTraceCounters(mTraceLastState, cur.states, STATE1_TRACE_MASK, @@ -2266,6 +2270,7 @@ public class BatteryStatsHistory { private int writeHistoryTag(HistoryTag tag) { if (tag.string == null) { Slog.wtfStack(TAG, "writeHistoryTag called with null name"); + tag.string = ""; } final int stringLength = tag.string.length(); diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java index dfb2884044f5..489721fbc10e 100644 --- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java @@ -105,7 +105,7 @@ public final class LongArrayMultiStateCounter implements Parcelable { public void setValues(long[] array) { if (array.length != mLength) { throw new IllegalArgumentException( - "Invalid array length: " + mLength + ", expected: " + mLength); + "Invalid array length: " + array.length + ", expected: " + mLength); } native_setValues(mNativeObject, array); } @@ -116,7 +116,7 @@ public final class LongArrayMultiStateCounter implements Parcelable { public void getValues(long[] array) { if (array.length != mLength) { throw new IllegalArgumentException( - "Invalid array length: " + mLength + ", expected: " + mLength); + "Invalid array length: " + array.length + ", expected: " + mLength); } native_getValues(mNativeObject, array); } @@ -347,6 +347,11 @@ public final class LongArrayMultiStateCounter implements Parcelable { throw new IllegalArgumentException( "State: " + state + ", outside the range: [0-" + mStateCount + "]"); } + if (longArrayContainer.mLength != mLength) { + throw new IllegalArgumentException( + "Invalid array length: " + longArrayContainer.mLength + + ", expected: " + mLength); + } native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state); } diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/core/java/com/android/internal/os/ProcfsMemoryUtil.java index 6cb6dc07f8b8..382f6c4a8a16 100644 --- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java +++ b/core/java/com/android/internal/os/ProcfsMemoryUtil.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.stats.pull; +package com.android.internal.os; -import static android.os.Process.PROC_OUT_STRING; +import static android.os.Process.*; import android.annotation.Nullable; import android.os.Process; @@ -23,6 +23,7 @@ import android.util.SparseArray; public final class ProcfsMemoryUtil { private static final int[] CMDLINE_OUT = new int[] { PROC_OUT_STRING }; + private static final int[] OOM_SCORE_ADJ_OUT = new int[] { PROC_NEWLINE_TERM | PROC_OUT_LONG }; private static final String[] STATUS_KEYS = new String[] { "Uid:", "VmHWM:", @@ -38,17 +39,34 @@ public final class ProcfsMemoryUtil { private ProcfsMemoryUtil() {} /** - * Reads memory stats of a process from procfs. Returns values of the VmHWM, VmRss, AnonRSS, - * VmSwap, RssShmem fields in /proc/pid/status in kilobytes or null if not available. + * Reads memory stats of a process from procfs. + * + * Returns values of the VmHWM, VmRss, AnonRSS, VmSwap, RssShmem fields in + * /proc/pid/status in kilobytes or null if not available. */ @Nullable public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) { + return readMemorySnapshotFromProcfs("/proc/" + pid + "/status"); + } + + /** + * Reads memory stats of the current process from procfs. + * + * Returns values of the VmHWM, VmRss, AnonRSS, VmSwap, RssShmem fields in + * /proc/self/status in kilobytes or null if not available. + */ + @Nullable + public static MemorySnapshot readMemorySnapshotFromProcfs() { + return readMemorySnapshotFromProcfs("/proc/self/status"); + } + + private static MemorySnapshot readMemorySnapshotFromProcfs(String path) { long[] output = new long[STATUS_KEYS.length]; output[0] = -1; output[3] = -1; output[4] = -1; output[5] = -1; - Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output); + Process.readProcLines(path, STATUS_KEYS, output); if (output[0] == -1 || output[3] == -1 || output[4] == -1 || output[5] == -1) { // Could not open or parse file. return null; @@ -70,14 +88,54 @@ public final class ProcfsMemoryUtil { * if the file is not available. */ public static String readCmdlineFromProcfs(int pid) { + return readCmdlineFromProcfs("/proc/" + pid + "/cmdline"); + } + + /** + * Reads cmdline of the current process from procfs. + * + * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string + * if the file is not available. + */ + public static String readCmdlineFromProcfs() { + return readCmdlineFromProcfs("/proc/self/cmdline"); + } + + private static String readCmdlineFromProcfs(String path) { String[] cmdline = new String[1]; - if (!Process.readProcFile("/proc/" + pid + "/cmdline", CMDLINE_OUT, cmdline, null, null)) { + if (!Process.readProcFile(path, CMDLINE_OUT, cmdline, null, null)) { return ""; } return cmdline[0]; } /** + * Reads oom_score_adj of a process from procfs + * + * Returns content of /proc/pid/oom_score_adj. Defaults to 0 if reading fails. + */ + public static int readOomScoreAdjFromProcfs(int pid) { + return readOomScoreAdjFromProcfs("/proc/" + pid + "/oom_score_adj"); + } + + /** + * Reads oom_score_adj of the current process from procfs + * + * Returns content of /proc/pid/oom_score_adj. Defaults to 0 if reading fails. + */ + public static int readOomScoreAdjFromProcfs() { + return readOomScoreAdjFromProcfs("/proc/self/oom_score_adj"); + } + + private static int readOomScoreAdjFromProcfs(String path) { + long[] oom_score_adj = new long[1]; + if (Process.readProcFile(path, OOM_SCORE_ADJ_OUT, null, oom_score_adj, null)) { + return (int)oom_score_adj[0]; + } + return 0; + } + + /** * Scans all /proc/pid/cmdline entries and returns a mapping between pid and cmdline. */ public static SparseArray<String> getProcessCmdlines() { @@ -109,7 +167,7 @@ public final class ProcfsMemoryUtil { /** Reads and parses selected entries of /proc/vmstat. */ @Nullable - static VmStat readVmStat() { + public static VmStat readVmStat() { long[] vmstat = new long[VMSTAT_KEYS.length]; vmstat[0] = -1; Process.readProcLines("/proc/vmstat", VMSTAT_KEYS, vmstat); @@ -121,7 +179,7 @@ public final class ProcfsMemoryUtil { return result; } - static final class VmStat { + public static final class VmStat { public int oomKillCount; } } diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java index b42ea7d0b769..e2237f61dfbb 100644 --- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java +++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java @@ -16,9 +16,15 @@ package com.android.internal.os.logging; +import android.app.Application; +import android.os.Process; +import android.util.Log; import android.view.WindowManager.LayoutParams; +import com.android.internal.os.ProcfsMemoryUtil; import com.android.internal.util.FrameworkStatsLog; +import java.util.Collection; +import libcore.util.NativeAllocationRegistry; /** * Used to wrap different logging calls in one, so that client side code base is clean and more @@ -49,4 +55,46 @@ public class MetricsLoggerWrapper { } } } + + public static void logPostGcMemorySnapshot() { + if (!com.android.libcore.Flags.nativeMetrics()) { + return; + } + int pid = Process.myPid(); + String processName = Application.getProcessName(); + Collection<NativeAllocationRegistry.Metrics> metrics = + NativeAllocationRegistry.getMetrics(); + int nMetrics = metrics.size(); + + String[] classNames = new String[nMetrics]; + long[] mallocedCount = new long[nMetrics]; + long[] mallocedBytes = new long[nMetrics]; + long[] nonmallocedCount = new long[nMetrics]; + long[] nonmallocedBytes = new long[nMetrics]; + + int i = 0; + for (NativeAllocationRegistry.Metrics m : metrics) { + classNames[i] = m.getClassName(); + mallocedCount[i] = m.getMallocedCount(); + mallocedBytes[i] = m.getMallocedBytes(); + nonmallocedCount[i] = m.getNonmallocedCount(); + nonmallocedBytes[i] = m.getNonmallocedBytes(); + i++; + } + + ProcfsMemoryUtil.MemorySnapshot m = ProcfsMemoryUtil.readMemorySnapshotFromProcfs(); + int oom_score_adj = ProcfsMemoryUtil.readOomScoreAdjFromProcfs(); + FrameworkStatsLog.write(FrameworkStatsLog.POSTGC_MEMORY_SNAPSHOT, + m.uid, processName, pid, + oom_score_adj, + m.rssInKilobytes, + m.anonRssInKilobytes, + m.swapInKilobytes, + m.anonRssInKilobytes + m.swapInKilobytes, + classNames, + mallocedCount, + mallocedBytes, + nonmallocedCount, + nonmallocedBytes); + } } diff --git a/core/java/com/android/internal/protolog/AutoClosableProtoInputStream.java b/core/java/com/android/internal/protolog/AutoClosableProtoInputStream.java new file mode 100644 index 000000000000..1acb34f73002 --- /dev/null +++ b/core/java/com/android/internal/protolog/AutoClosableProtoInputStream.java @@ -0,0 +1,56 @@ +/* + * 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.protolog; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.proto.ProtoInputStream; + +import java.io.FileInputStream; +import java.io.IOException; + +public final class AutoClosableProtoInputStream implements AutoCloseable { + @NonNull + private final ProtoInputStream mProtoInputStream; + @Nullable + private final FileInputStream mFileInputStream; + + public AutoClosableProtoInputStream(@NonNull FileInputStream fileInputStream) { + mProtoInputStream = new ProtoInputStream(fileInputStream); + mFileInputStream = fileInputStream; + } + + public AutoClosableProtoInputStream(@NonNull byte[] input) { + mProtoInputStream = new ProtoInputStream(input); + mFileInputStream = null; + } + + /** + * @return the ProtoInputStream this class is wrapping + */ + @NonNull + public ProtoInputStream get() { + return mProtoInputStream; + } + + @Override + public void close() throws IOException { + if (mFileInputStream != null) { + mFileInputStream.close(); + } + } +} diff --git a/core/java/com/android/internal/protolog/NoViewerConfigProtoLogImpl.java b/core/java/com/android/internal/protolog/NoViewerConfigProtoLogImpl.java new file mode 100644 index 000000000000..1598766412dd --- /dev/null +++ b/core/java/com/android/internal/protolog/NoViewerConfigProtoLogImpl.java @@ -0,0 +1,90 @@ +/* + * 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.protolog; + +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.protolog.common.ILogger; +import com.android.internal.protolog.common.IProtoLog; +import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.internal.protolog.common.LogLevel; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Class should only be used as a temporary solution to missing viewer config file on device. + * In particular this class should only be initialized in Robolectric tests, if it's being used + * otherwise please report it. + * + * @deprecated + */ +@Deprecated +public class NoViewerConfigProtoLogImpl implements IProtoLog { + private static final String LOG_TAG = "ProtoLog"; + + @Override + public void log(LogLevel logLevel, IProtoLogGroup group, long messageHash, int paramsMask, + Object[] args) { + Log.w(LOG_TAG, "ProtoLogging is not available due to missing viewer config file..."); + logMessage(logLevel, group.getTag(), "PROTOLOG#" + messageHash + "(" + + Arrays.stream(args).map(Object::toString).collect(Collectors.joining()) + ")"); + } + + @Override + public void log(LogLevel logLevel, IProtoLogGroup group, String messageString, Object... args) { + logMessage(logLevel, group.getTag(), TextUtils.formatSimple(messageString, args)); + } + + @Override + public boolean isProtoEnabled() { + return false; + } + + @Override + public int startLoggingToLogcat(String[] groups, ILogger logger) { + return 0; + } + + @Override + public int stopLoggingToLogcat(String[] groups, ILogger logger) { + return 0; + } + + @Override + public boolean isEnabled(IProtoLogGroup group, LogLevel level) { + return false; + } + + @Override + public List<IProtoLogGroup> getRegisteredGroups() { + return List.of(); + } + + private void logMessage(LogLevel logLevel, String tag, String message) { + switch (logLevel) { + case VERBOSE -> Log.v(tag, message); + case INFO -> Log.i(tag, message); + case DEBUG -> Log.d(tag, message); + case WARN -> Log.w(tag, message); + case ERROR -> Log.e(tag, message); + case WTF -> Log.wtf(tag, message); + } + } +} diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index a037cb421b0c..a1c987f79304 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -60,18 +60,16 @@ import android.util.ArraySet; import android.util.Log; import android.util.LongArray; import android.util.Slog; -import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.RegisterClientArgs; import com.android.internal.protolog.common.ILogger; import com.android.internal.protolog.common.IProtoLog; import com.android.internal.protolog.common.IProtoLogGroup; import com.android.internal.protolog.common.LogDataType; import com.android.internal.protolog.common.LogLevel; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; @@ -93,26 +91,18 @@ import java.util.stream.Stream; /** * A service for the ProtoLog logging system. */ -public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProtoLog { +public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProtoLog { private static final String LOG_TAG = "ProtoLog"; public static final String NULL_STRING = "null"; private final AtomicInteger mTracingInstances = new AtomicInteger(); @NonNull - private final ProtoLogDataSource mDataSource; - @Nullable - private final ProtoLogViewerConfigReader mViewerConfigReader; - @Deprecated - @Nullable - private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider; + protected final ProtoLogDataSource mDataSource; @NonNull - private final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>(); + protected final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>(); @NonNull private final Runnable mCacheUpdater; - @Nullable // null when the flag android.tracing.client_side_proto_logging is not flipped - private final IProtoLogConfigurationService mProtoLogConfigurationService; - @NonNull private final int[] mDefaultLogLevelCounts = new int[LogLevel.values().length]; @NonNull @@ -121,68 +111,15 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto private final Map<String, Integer> mCollectStackTraceGroupCounts = new ArrayMap<>(); private final Lock mBackgroundServiceLock = new ReentrantLock(); - private ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor(); - - public PerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups) - throws ServiceManager.ServiceNotFoundException { - this(null, null, null, () -> {}, groups); - } - - public PerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups) - throws ServiceManager.ServiceNotFoundException { - this(null, null, null, cacheUpdater, groups); - } - - public PerfettoProtoLogImpl( - @NonNull String viewerConfigFilePath, - @NonNull Runnable cacheUpdater, - @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - this(viewerConfigFilePath, - null, - new ProtoLogViewerConfigReader(() -> { - try { - return new ProtoInputStream(new FileInputStream(viewerConfigFilePath)); - } catch (FileNotFoundException e) { - throw new RuntimeException( - "Failed to load viewer config file " + viewerConfigFilePath, e); - } - }), - cacheUpdater, groups); - } - - @Deprecated - @VisibleForTesting - public PerfettoProtoLogImpl( - @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, - @Nullable ProtoLogViewerConfigReader viewerConfigReader, - @NonNull Runnable cacheUpdater, - @NonNull IProtoLogGroup[] groups, - @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, - @NonNull ProtoLogConfigurationService configurationService) { - this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater, - groups, dataSourceBuilder, configurationService); - } + protected ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor(); - @VisibleForTesting - public PerfettoProtoLogImpl( - @Nullable String viewerConfigFilePath, - @Nullable ProtoLogViewerConfigReader viewerConfigReader, - @NonNull Runnable cacheUpdater, - @NonNull IProtoLogGroup[] groups, - @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, - @NonNull ProtoLogConfigurationService configurationService) { - this(viewerConfigFilePath, null, viewerConfigReader, cacheUpdater, - groups, dataSourceBuilder, configurationService); - } + // Set to true once this is ready to accept protolog to logcat requests. + private boolean mLogcatReady = false; - private PerfettoProtoLogImpl( - @Nullable String viewerConfigFilePath, - @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, - @Nullable ProtoLogViewerConfigReader viewerConfigReader, + protected PerfettoProtoLogImpl( @NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - this(viewerConfigFilePath, viewerConfigInputStreamProvider, viewerConfigReader, - cacheUpdater, groups, + this(cacheUpdater, groups, ProtoLogDataSource::new, android.tracing.Flags.clientSideProtoLogging() ? IProtoLogConfigurationService.Stub.asInterface( @@ -191,19 +128,11 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto ); } - private PerfettoProtoLogImpl( - @Nullable String viewerConfigFilePath, - @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, - @Nullable ProtoLogViewerConfigReader viewerConfigReader, + protected PerfettoProtoLogImpl( @NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups, @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, @Nullable IProtoLogConfigurationService configurationService) { - if (viewerConfigFilePath != null && viewerConfigInputStreamProvider != null) { - throw new RuntimeException("Only one of viewerConfigFilePath and " - + "viewerConfigInputStreamProvider can be set"); - } - mDataSource = dataSourceBuilder.build( this::onTracingInstanceStart, this::onTracingFlush, @@ -219,55 +148,33 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto // for some messages logged right after the construction of this class. mDataSource.register(params); - if (viewerConfigInputStreamProvider == null && viewerConfigFilePath != null) { - viewerConfigInputStreamProvider = new ViewerConfigInputStreamProvider() { - @NonNull - @Override - public ProtoInputStream getInputStream() { - try { - return new ProtoInputStream(new FileInputStream(viewerConfigFilePath)); - } catch (FileNotFoundException e) { - throw new RuntimeException( - "Failed to load viewer config file " + viewerConfigFilePath, e); - } - } - }; - } - - this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; - this.mViewerConfigReader = viewerConfigReader; this.mCacheUpdater = cacheUpdater; registerGroupsLocally(groups); if (android.tracing.Flags.clientSideProtoLogging()) { - mProtoLogConfigurationService = configurationService; - Objects.requireNonNull(mProtoLogConfigurationService, + Objects.requireNonNull(configurationService, "A null ProtoLog Configuration Service was provided!"); try { - var args = new ProtoLogConfigurationServiceImpl.RegisterClientArgs(); - - if (viewerConfigFilePath != null) { - args.setViewerConfigFile(viewerConfigFilePath); - } + var args = createConfigurationServiceRegisterClientArgs(); final var groupArgs = Stream.of(groups) - .map(group -> new ProtoLogConfigurationServiceImpl.RegisterClientArgs + .map(group -> new RegisterClientArgs .GroupConfig(group.name(), group.isLogToLogcat())) - .toArray(ProtoLogConfigurationServiceImpl - .RegisterClientArgs.GroupConfig[]::new); + .toArray(RegisterClientArgs.GroupConfig[]::new); args.setGroups(groupArgs); - mProtoLogConfigurationService.registerClient(this, args); + configurationService.registerClient(this, args); } catch (RemoteException e) { throw new RuntimeException("Failed to register ProtoLog client"); } - } else { - mProtoLogConfigurationService = null; } } + @NonNull + protected abstract RegisterClientArgs createConfigurationServiceRegisterClientArgs(); + /** * Main log method, do not call directly. */ @@ -334,9 +241,6 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto * @return status code */ public int startLoggingToLogcat(String[] groups, @NonNull ILogger logger) { - if (mViewerConfigReader != null) { - mViewerConfigReader.loadViewerConfig(groups, logger); - } return setTextLogging(true, logger, groups); } @@ -347,9 +251,6 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto * @return status code */ public int stopLoggingToLogcat(String[] groups, @NonNull ILogger logger) { - if (mViewerConfigReader != null) { - mViewerConfigReader.unloadViewerConfig(groups, logger); - } return setTextLogging(false, logger, groups); } @@ -372,21 +273,8 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto // we might want to manually specify an id for the group with a collision verifyNoCollisionsOrDuplicates(protoLogGroups); - final var groupsLoggingToLogcat = new ArrayList<String>(); for (IProtoLogGroup protoLogGroup : protoLogGroups) { mLogGroups.put(protoLogGroup.name(), protoLogGroup); - - if (protoLogGroup.isLogToLogcat()) { - groupsLoggingToLogcat.add(protoLogGroup.name()); - } - } - - if (mViewerConfigReader != null) { - // Load in background to avoid delay in boot process. - // The caveat is that any log message that is also logged to logcat will not be - // successfully decoded until this completes. - mBackgroundLoggingService.execute(() -> mViewerConfigReader - .loadViewerConfig(groupsLoggingToLogcat.toArray(new String[0]))); } } @@ -403,6 +291,10 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto } } + protected void readyToLogToLogcat() { + mLogcatReady = true; + } + /** * Responds to a shell command. */ @@ -499,57 +391,21 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto } @Deprecated - private void dumpViewerConfig() { - if (mViewerConfigInputStreamProvider == null) { - // No viewer config available - return; - } - - Log.d(LOG_TAG, "Dumping viewer config to trace"); + abstract void dumpViewerConfig(); - Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider); - - Log.d(LOG_TAG, "Dumped viewer config to trace"); - } + @NonNull + abstract String getLogcatMessageString(@NonNull Message message); - private void logToLogcat(String tag, LogLevel level, Message message, + private void logToLogcat(@NonNull String tag, @NonNull LogLevel level, @NonNull Message message, @Nullable Object[] args) { - String messageString; - if (mViewerConfigReader == null) { - messageString = message.getMessage(); - - if (messageString == null) { - Log.e(LOG_TAG, "Failed to decode message for logcat. " - + "Message not available without ViewerConfig to decode the hash."); - } - } else { - messageString = message.getMessage(mViewerConfigReader); - - if (messageString == null) { - Log.e(LOG_TAG, "Failed to decode message for logcat. " - + "Message hash either not available in viewerConfig file or " - + "not loaded into memory from file before decoding."); - } - } - - if (messageString == null) { - StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE"); - if (args != null) { - builder.append(" args = ("); - builder.append(String.join(", ", Arrays.stream(args) - .map(it -> { - if (it == null) { - return "null"; - } else { - return it.toString(); - } - }).toList())); - builder.append(")"); - } - messageString = builder.toString(); - args = new Object[0]; + if (!mLogcatReady) { + Log.w(LOG_TAG, "Trying to log a protolog message with hash " + + message.getMessageHash() + " to logcat before the service is ready to accept " + + "such requests."); + return; } + String messageString = getLogcatMessageString(message); logToLogcat(tag, level, messageString, args); } diff --git a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java new file mode 100644 index 000000000000..febe1f3a72ac --- /dev/null +++ b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java @@ -0,0 +1,172 @@ +/* + * 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.protolog; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.ServiceManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.RegisterClientArgs; +import com.android.internal.protolog.common.ILogger; +import com.android.internal.protolog.common.IProtoLogGroup; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.ArrayList; + +public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl { + private static final String LOG_TAG = "PerfettoProtoLogImpl"; + + @NonNull + private final ProtoLogViewerConfigReader mViewerConfigReader; + @Deprecated + @NonNull + private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider; + @NonNull + private final String mViewerConfigFilePath; + + public ProcessedPerfettoProtoLogImpl( + @NonNull String viewerConfigFilePath, + @NonNull Runnable cacheUpdater, + @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { + this(viewerConfigFilePath, new ViewerConfigInputStreamProvider() { + @NonNull + @Override + public AutoClosableProtoInputStream getInputStream() { + try { + final var protoFileInputStream = + new FileInputStream(viewerConfigFilePath); + return new AutoClosableProtoInputStream(protoFileInputStream); + } catch (FileNotFoundException e) { + throw new RuntimeException( + "Failed to load viewer config file " + viewerConfigFilePath, e); + } + } + }, + cacheUpdater, groups); + } + + @VisibleForTesting + public ProcessedPerfettoProtoLogImpl( + @NonNull String viewerConfigFilePath, + @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, + @NonNull Runnable cacheUpdater, + @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { + super(cacheUpdater, groups); + + this.mViewerConfigFilePath = viewerConfigFilePath; + + this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; + this.mViewerConfigReader = new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider); + + loadLogcatGroupsViewerConfig(groups); + } + + @VisibleForTesting + public ProcessedPerfettoProtoLogImpl( + @NonNull String viewerConfigFilePath, + @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, + @NonNull ProtoLogViewerConfigReader viewerConfigReader, + @NonNull Runnable cacheUpdater, + @NonNull IProtoLogGroup[] groups, + @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, + @Nullable IProtoLogConfigurationService configurationService) + throws ServiceManager.ServiceNotFoundException { + super(cacheUpdater, groups, dataSourceBuilder, configurationService); + + this.mViewerConfigFilePath = viewerConfigFilePath; + + this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; + this.mViewerConfigReader = viewerConfigReader; + + loadLogcatGroupsViewerConfig(groups); + } + + @NonNull + @Override + protected RegisterClientArgs createConfigurationServiceRegisterClientArgs() { + return new RegisterClientArgs() + .setViewerConfigFile(mViewerConfigFilePath); + } + + /** + * Start text logging + * @param groups Groups to start text logging for + * @param logger A logger to write status updates to + * @return status code + */ + @Override + public int startLoggingToLogcat(String[] groups, @NonNull ILogger logger) { + mViewerConfigReader.loadViewerConfig(groups, logger); + return super.startLoggingToLogcat(groups, logger); + } + + /** + * Stop text logging + * @param groups Groups to start text logging for + * @param logger A logger to write status updates to + * @return status code + */ + @Override + public int stopLoggingToLogcat(String[] groups, @NonNull ILogger logger) { + mViewerConfigReader.unloadViewerConfig(groups, logger); + return super.stopLoggingToLogcat(groups, logger); + } + + @Deprecated + @Override + void dumpViewerConfig() { + Log.d(LOG_TAG, "Dumping viewer config to trace"); + Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider); + Log.d(LOG_TAG, "Dumped viewer config to trace"); + } + + @NonNull + @Override + String getLogcatMessageString(@NonNull Message message) { + String messageString; + messageString = message.getMessage(mViewerConfigReader); + + if (messageString == null) { + throw new RuntimeException("Failed to decode message for logcat. " + + "Message hash (" + message.getMessageHash() + ") either not available in " + + "viewerConfig file (" + mViewerConfigFilePath + ") or " + + "not loaded into memory from file before decoding."); + } + + return messageString; + } + + private void loadLogcatGroupsViewerConfig(@NonNull IProtoLogGroup[] protoLogGroups) { + final var groupsLoggingToLogcat = new ArrayList<String>(); + for (IProtoLogGroup protoLogGroup : protoLogGroups) { + if (protoLogGroup.isLogToLogcat()) { + groupsLoggingToLogcat.add(protoLogGroup.name()); + } + } + + // Load in background to avoid delay in boot process. + // The caveat is that any log message that is also logged to logcat will not be + // successfully decoded until this completes. + mBackgroundLoggingService.execute(() -> { + mViewerConfigReader.loadViewerConfig(groupsLoggingToLogcat.toArray(new String[0])); + readyToLogToLogcat(); + }); + } +} diff --git a/core/java/com/android/internal/protolog/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java index 60213b1830c6..d117e93d7de7 100644 --- a/core/java/com/android/internal/protolog/ProtoLog.java +++ b/core/java/com/android/internal/protolog/ProtoLog.java @@ -70,16 +70,16 @@ public class ProtoLog { // directly to the generated tracing implementations. if (android.tracing.Flags.perfettoProtologTracing()) { synchronized (sInitLock) { + final var allGroups = new HashSet<>(Arrays.stream(groups).toList()); if (sProtoLogInstance != null) { // The ProtoLog instance has already been initialized in this process final var alreadyRegisteredGroups = sProtoLogInstance.getRegisteredGroups(); - final var allGroups = new HashSet<>(alreadyRegisteredGroups); - allGroups.addAll(Arrays.stream(groups).toList()); - groups = allGroups.toArray(new IProtoLogGroup[0]); + allGroups.addAll(alreadyRegisteredGroups); } try { - sProtoLogInstance = new PerfettoProtoLogImpl(groups); + sProtoLogInstance = new UnprocessedPerfettoProtoLogImpl( + allGroups.toArray(new IProtoLogGroup[0])); } catch (ServiceManager.ServiceNotFoundException e) { throw new RuntimeException(e); } diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java index 8d37899f8602..e9a8770deb73 100644 --- a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java @@ -379,7 +379,7 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ @NonNull String viewerConfigFilePath) { Utils.dumpViewerConfig(dataSource, () -> { try { - return new ProtoInputStream(new FileInputStream(viewerConfigFilePath)); + return new AutoClosableProtoInputStream(new FileInputStream(viewerConfigFilePath)); } catch (FileNotFoundException e) { throw new RuntimeException( "Failed to load viewer config file " + viewerConfigFilePath, e); diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java index 5d67534b1b44..3378d08e7761 100644 --- a/core/java/com/android/internal/protolog/ProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java @@ -105,31 +105,10 @@ public class ProtoLogImpl { + "viewerConfigPath = " + sViewerConfigPath); final var groups = sLogGroups.values().toArray(new IProtoLogGroup[0]); - if (android.tracing.Flags.perfettoProtologTracing()) { - try { - File f = new File(sViewerConfigPath); - if (!ProtoLog.REQUIRE_PROTOLOGTOOL && !f.exists()) { - // TODO(b/353530422): Remove - temporary fix to unblock b/352290057 - // In some tests the viewer config file might not exist in which we don't - // want to provide config path to the user - Log.w(LOG_TAG, "Failed to find viewerConfigFile when setting up " - + ProtoLogImpl.class.getSimpleName() + ". " - + "Setting up without a viewer config instead..."); - - sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater, groups); - } else { - sServiceInstance = - new PerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater, groups); - } - } catch (ServiceManager.ServiceNotFoundException e) { - throw new RuntimeException(e); - } + sServiceInstance = createProtoLogImpl(groups); } else { - var protologImpl = new LegacyProtoLogImpl( - sLegacyOutputFilePath, sLegacyViewerConfigPath, sCacheUpdater); - protologImpl.registerGroups(groups); - sServiceInstance = protologImpl; + sServiceInstance = createLegacyProtoLogImpl(groups); } sCacheUpdater.run(); @@ -137,6 +116,34 @@ public class ProtoLogImpl { return sServiceInstance; } + private static IProtoLog createProtoLogImpl(IProtoLogGroup[] groups) { + try { + File f = new File(sViewerConfigPath); + if (!f.exists()) { + // TODO(b/353530422): Remove - temporary fix to unblock b/352290057 + // In robolectric tests the viewer config file isn't current available, so we cannot + // use the ProcessedPerfettoProtoLogImpl. + Log.e(LOG_TAG, "Failed to find viewer config file " + sViewerConfigPath + + " when setting up " + ProtoLogImpl.class.getSimpleName() + ". " + + "ProtoLog will not work here!"); + + return new NoViewerConfigProtoLogImpl(); + } else { + return new ProcessedPerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater, groups); + } + } catch (ServiceManager.ServiceNotFoundException e) { + throw new RuntimeException(e); + } + } + + private static LegacyProtoLogImpl createLegacyProtoLogImpl(IProtoLogGroup[] groups) { + var protologImpl = new LegacyProtoLogImpl( + sLegacyOutputFilePath, sLegacyViewerConfigPath, sCacheUpdater); + protologImpl.registerGroups(groups); + + return protologImpl; + } + @VisibleForTesting public static synchronized void setSingleInstance(@Nullable IProtoLog instance) { sServiceInstance = instance; diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java index 571fe0ba37b2..524f64225084 100644 --- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java +++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java @@ -106,46 +106,47 @@ public class ProtoLogViewerConfigReader { long targetGroupId = loadGroupId(group); final Map<Long, String> hashesForGroup = new TreeMap<>(); - final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream(); - - while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - if (pis.getFieldNumber() == (int) MESSAGES) { - final long inMessageToken = pis.start(MESSAGES); - - long messageId = 0; - String message = null; - int groupId = 0; - while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - switch (pis.getFieldNumber()) { - case (int) MESSAGE_ID: - messageId = pis.readLong(MESSAGE_ID); - break; - case (int) MESSAGE: - message = pis.readString(MESSAGE); - break; - case (int) GROUP_ID: - groupId = pis.readInt(GROUP_ID); - break; + try (var pisWrapper = mViewerConfigInputStreamProvider.getInputStream()) { + final var pis = pisWrapper.get(); + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (pis.getFieldNumber() == (int) MESSAGES) { + final long inMessageToken = pis.start(MESSAGES); + + long messageId = 0; + String message = null; + int groupId = 0; + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pis.getFieldNumber()) { + case (int) MESSAGE_ID: + messageId = pis.readLong(MESSAGE_ID); + break; + case (int) MESSAGE: + message = pis.readString(MESSAGE); + break; + case (int) GROUP_ID: + groupId = pis.readInt(GROUP_ID); + break; + } } - } - if (groupId == 0) { - throw new IOException("Failed to get group id"); - } + if (groupId == 0) { + throw new IOException("Failed to get group id"); + } - if (messageId == 0) { - throw new IOException("Failed to get message id"); - } + if (messageId == 0) { + throw new IOException("Failed to get message id"); + } - if (message == null) { - throw new IOException("Failed to get message string"); - } + if (message == null) { + throw new IOException("Failed to get message string"); + } - if (groupId == targetGroupId) { - hashesForGroup.put(messageId, message); - } + if (groupId == targetGroupId) { + hashesForGroup.put(messageId, message); + } - pis.end(inMessageToken); + pis.end(inMessageToken); + } } } @@ -153,30 +154,32 @@ public class ProtoLogViewerConfigReader { } private long loadGroupId(@NonNull String group) throws IOException { - final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream(); - - while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - if (pis.getFieldNumber() == (int) GROUPS) { - final long inMessageToken = pis.start(GROUPS); - - long groupId = 0; - String groupName = null; - while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - switch (pis.getFieldNumber()) { - case (int) ID: - groupId = pis.readInt(ID); - break; - case (int) NAME: - groupName = pis.readString(NAME); - break; + try (var pisWrapper = mViewerConfigInputStreamProvider.getInputStream()) { + final var pis = pisWrapper.get(); + + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (pis.getFieldNumber() == (int) GROUPS) { + final long inMessageToken = pis.start(GROUPS); + + long groupId = 0; + String groupName = null; + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pis.getFieldNumber()) { + case (int) ID: + groupId = pis.readInt(ID); + break; + case (int) NAME: + groupName = pis.readString(NAME); + break; + } } - } - if (Objects.equals(groupName, group)) { - return groupId; - } + if (Objects.equals(groupName, group)) { + return groupId; + } - pis.end(inMessageToken); + pis.end(inMessageToken); + } } } diff --git a/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java new file mode 100644 index 000000000000..f3fe58070fa9 --- /dev/null +++ b/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java @@ -0,0 +1,61 @@ +/* + * 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.protolog; + +import android.annotation.NonNull; +import android.os.ServiceManager; + +import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.RegisterClientArgs; +import com.android.internal.protolog.common.IProtoLogGroup; + +public class UnprocessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl { + public UnprocessedPerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups) + throws ServiceManager.ServiceNotFoundException { + this(() -> {}, groups); + } + + public UnprocessedPerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, + @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { + super(cacheUpdater, groups); + readyToLogToLogcat(); + } + + @NonNull + @Override + protected RegisterClientArgs createConfigurationServiceRegisterClientArgs() { + return new RegisterClientArgs(); + } + + @Override + void dumpViewerConfig() { + // No-op + } + + @NonNull + @Override + String getLogcatMessageString(@NonNull Message message) { + String messageString; + messageString = message.getMessage(); + + if (messageString == null) { + throw new RuntimeException("Failed to decode message for logcat. " + + "Message not available without ViewerConfig to decode the hash."); + } + + return messageString; + } +} diff --git a/core/java/com/android/internal/protolog/Utils.java b/core/java/com/android/internal/protolog/Utils.java index 00ef80ab2bdd..629682ca2e71 100644 --- a/core/java/com/android/internal/protolog/Utils.java +++ b/core/java/com/android/internal/protolog/Utils.java @@ -48,8 +48,8 @@ public class Utils { public static void dumpViewerConfig(@NonNull ProtoLogDataSource dataSource, @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) { dataSource.trace(ctx -> { - try { - ProtoInputStream pis = viewerConfigInputStreamProvider.getInputStream(); + try (var pisWrapper = viewerConfigInputStreamProvider.getInputStream()) { + final var pis = pisWrapper.get(); final ProtoOutputStream os = ctx.newTracePacket(); diff --git a/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java b/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java index 14bc8e4782f2..60c98923eb23 100644 --- a/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java +++ b/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java @@ -17,12 +17,12 @@ package com.android.internal.protolog; import android.annotation.NonNull; -import android.util.proto.ProtoInputStream; public interface ViewerConfigInputStreamProvider { /** * @return a ProtoInputStream. */ @NonNull - ProtoInputStream getInputStream(); + AutoClosableProtoInputStream getInputStream(); } + diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index 81b885aa626b..b5c87868af12 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -84,4 +84,5 @@ oneway interface IPhoneStateListener { void onSimultaneousCallingStateChanged(in int[] subIds); void onCarrierRoamingNtnModeChanged(in boolean active); void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible); + void onCarrierRoamingNtnAvailableServicesChanged(in int[] availableServices); } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index f836cf2b9d87..ca75abdedfcc 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -123,4 +123,5 @@ interface ITelephonyRegistry { void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason); void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active); void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible); + void notifyCarrierRoamingNtnAvailableServicesChanged(int subId, in int[] availableServices); } diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 70a80b2bd4f1..76f66cd4ebc9 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -1024,10 +1024,18 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p 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[] = { + "--dirty-image-objects=/apex/com.android.art/etc/dirty-image-objects", + "--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/res/res/values/config.xml b/core/res/res/values/config.xml index 41981715a855..9c92e5ca589b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -7175,4 +7175,10 @@ <string name="identity_check_settings_action"></string> <!-- Package for opening identity check settings page [CHAR LIMIT=NONE] [DO NOT TRANSLATE] --> <string name="identity_check_settings_package_name">com\u002eandroid\u002esettings</string> + + <!-- The name of the service for forensic backup transport. --> + <string name="config_forensicBackupTransport" translatable="false"></string> + + <!-- Whether to enable fp unlock when screen turns off on udfps devices --> + <bool name="config_screen_off_udfps_enabled">false</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0b2b3453f20a..712b99439496 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5636,4 +5636,10 @@ <!-- Identity check strings --> <java-symbol type="string" name="identity_check_settings_action" /> <java-symbol type="string" name="identity_check_settings_package_name" /> + + <!-- Forensic backup transport --> + <java-symbol type="string" name="config_forensicBackupTransport" /> + + <!-- Fingerprint screen off unlock config --> + <java-symbol type="bool" name="config_screen_off_udfps_enabled" /> </resources> diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java index 9d477094692a..d5479ea3310d 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java @@ -358,7 +358,6 @@ public class SQLiteDatabaseTest { assertTrue("ReadThread failed with errors: " + errors, errors.isEmpty()); } - @RequiresFlagsEnabled(Flags.FLAG_SQLITE_ALLOW_TEMP_TABLES) @Test public void testTempTable() { boolean allowed; diff --git a/core/tests/vibrator/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java index 04945f38e319..9099918edb02 100644 --- a/core/tests/vibrator/src/android/os/VibratorInfoTest.java +++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java @@ -71,8 +71,7 @@ public class VibratorInfoTest { VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); assertFalse(noCapabilities.hasFrequencyControl()); VibratorInfo composeAndFrequencyControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setCapabilities( - IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) .build(); assertTrue(composeAndFrequencyControl.hasFrequencyControl()); } @@ -153,7 +152,8 @@ public class VibratorInfoTest { VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); assertFalse(noCapabilities.areEnvelopeEffectsSupported()); VibratorInfo envelopeEffectCapability = new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2) + .setCapabilities( + IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2) .build(); assertTrue(envelopeEffectCapability.areEnvelopeEffectsSupported()); } diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp index 4edf52bae161..0964aab43380 100644 --- a/data/fonts/Android.bp +++ b/data/fonts/Android.bp @@ -53,22 +53,9 @@ soong_config_bool_variable { name: "use_var_font", } -soong_config_module_type { - name: "prebuilt_fonts_xml", - module_type: "prebuilt_etc", - config_namespace: "noto_sans_cjk_config", - bool_variables: ["use_var_font"], - properties: ["src"], -} - -prebuilt_fonts_xml { +prebuilt_etc { name: "fonts.xml", src: "fonts.xml", - soong_config_variables: { - use_var_font: { - src: "fonts_cjkvf.xml", - }, - }, } prebuilt_etc { diff --git a/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml deleted file mode 100644 index ae50da18119f..000000000000 --- a/data/fonts/font_fallback.xml +++ /dev/null @@ -1,950 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - In this file, all fonts without names are added to the default list. - Fonts are chosen based on a match: full BCP-47 language tag including - script, then just language, and finally order (the first font containing - the glyph). - - Order of appearance is also the tiebreaker for weight matching. This is - the reason why the 900 weights of Roboto precede the 700 weights - we - prefer the former when an 800 weight is requested. Since bold spans - effectively add 300 to the weight, this ensures that 900 is the bold - paired with the 500 weight, ensuring adequate contrast. - - - The font_fallback.xml defines the list of font used by the system. - - `familyset` node: - A `familyset` element must be a root node of the font_fallback.xml. No attributes are allowed - to `familyset` node. - The `familyset` node can contains `family` and `alias` nodes. Any other nodes will be ignored. - - `family` node: - A `family` node defines a single font family definition. - A font family is a set of fonts for drawing text in various styles such as weight, slant. - There are three types of families, default family, named family and locale fallback family. - - The default family is a special family node appeared the first node of the `familyset` node. - The default family is used as first priority fallback. - Only `name` attribute can be used for default family node. If the `name` attribute is - specified, This family will also works as named family. - - The named family is a family that has name attribute. The named family defines a new fallback. - For example, if the name attribute is "serif", it creates serif fallback. Developers can - access the fallback by using Typeface#create API. - The named family can not have attribute other than `name` attribute. The `name` attribute - cannot be empty. - - The locale fallback family is a font family that is used for fallback. The fallback family is - used when the named family or default family cannot be used. The locale fallback family can - have `lang` attribute and `variant` attribute. The `lang` attribute is an optional comma - separated BCP-47i language tag. The `variant` is an optional attribute that can be one one - `element`, `compact`. If a `variant` attribute is not specified, it is treated as default. - - `alias` node: - An `alias` node defines a alias of named family with changing weight offset. An `alias` node - can have mandatory `name` and `to` attribute and optional `weight` attribute. This `alias` - defines new fallback that has the name of specified `name` attribute. The fallback list is - the same to the fallback that of the name specified with `to` attribute. If `weight` attribute - is specified, the base weight offset is shifted to the specified value. For example, if the - `weight` is 500, the output text is drawn with 500 of weight. - - `font` node: - A `font` node defines a single font definition. There are two types of fonts, static font and - variable font. - - A static font can have `weight`, `style`, `index` and `postScriptName` attributes. A `weight` - is a mandatory attribute that defines the weight of the font. Any number between 0 to 1000 is - valid. A `style` is a mandatory attribute that defines the style of the font. A 'style' - attribute can be `normal` or `italic`. An `index` is an optional attribute that defines the - index of the font collection. If this is not specified, it is treated as 0. If the font file - is not a font collection, this attribute is ignored. A `postScriptName` attribute is an - optional attribute. A PostScript name is used for identifying target of system font update. - If this is not specified, the system assumes the filename is same to PostScript name of the - font file. For example, if the font file is "Roboto-Regular.ttf", the system assume the - PostScript name of this font is "Roboto-Regular". - - A variable font can be only defined for the variable font file. A variable font can have - `axis` child nodes for specifying axis values. A variable font can have all attribute of - static font and can have additional `supportedAxes` attribute. A `supportedAxes` attribute - is a comma separated supported axis tags. As of Android V, only `wght` and `ital` axes can - be specified. - - If `supportedAxes` attribute is not specified, this `font` node works as static font of the - single instance of variable font specified with `axis` children. - - If `supportedAxes` attribute is specified, the system dynamically create font instance for the - given weight and style value. If `wght` is specified in `supportedAxes` attribute the `weight` - attribute and `axis` child that has `wght` tag become optional and ignored because it is - determined by system at runtime. Similarly, if `ital` is specified in `supportedAxes` - attribute, the `style` attribute and `axis` child that has `ital` tag become optional and - ignored. - - `axis` node: - An `axis` node defines a font variation value for a tag. An `axis` node can have two mandatory - attributes, `tag` and `value`. If the font is variable font and the same tag `axis` node is - specified in `supportedAxes` attribute, the style value works like a default instance. ---> -<familyset> - <!-- first font is default --> - <family name="sans-serif"> - <font supportedAxes="wght,ital">Roboto-Regular.ttf - <axis tag="wdth" stylevalue="100" /> - </font> - </family> - - - <!-- Note that aliases must come after the fonts they reference. --> - <alias name="sans-serif-thin" to="sans-serif" weight="100" /> - <alias name="sans-serif-light" to="sans-serif" weight="300" /> - <alias name="sans-serif-medium" to="sans-serif" weight="500" /> - <alias name="sans-serif-black" to="sans-serif" weight="900" /> - <alias name="arial" to="sans-serif" /> - <alias name="helvetica" to="sans-serif" /> - <alias name="tahoma" to="sans-serif" /> - <alias name="verdana" to="sans-serif" /> - - <family name="sans-serif-condensed"> - <font supportedAxes="wght,ital">Roboto-Regular.ttf - <axis tag="wdth" stylevalue="75" /> - </font> - </family> - <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" /> - <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" /> - - <family name="serif"> - <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font> - <font weight="700" style="normal">NotoSerif-Bold.ttf</font> - <font weight="400" style="italic">NotoSerif-Italic.ttf</font> - <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font> - </family> - <alias name="serif-bold" to="serif" weight="700" /> - <alias name="times" to="serif" /> - <alias name="times new roman" to="serif" /> - <alias name="palatino" to="serif" /> - <alias name="georgia" to="serif" /> - <alias name="baskerville" to="serif" /> - <alias name="goudy" to="serif" /> - <alias name="fantasy" to="serif" /> - <alias name="ITC Stone Serif" to="serif" /> - - <family name="monospace"> - <font weight="400" style="normal">DroidSansMono.ttf</font> - </family> - <alias name="sans-serif-monospace" to="monospace" /> - <alias name="monaco" to="monospace" /> - - <family name="serif-monospace"> - <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font> - </family> - <alias name="courier" to="serif-monospace" /> - <alias name="courier new" to="serif-monospace" /> - - <family name="casual"> - <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font> - </family> - - <family name="cursive"> - <font supportedAxes="wght">DancingScript-Regular.ttf</font> - </family> - - <family name="sans-serif-smallcaps"> - <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font> - </family> - - <family name="source-sans-pro"> - <font weight="400" style="normal">SourceSansPro-Regular.ttf</font> - <font weight="400" style="italic">SourceSansPro-Italic.ttf</font> - <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font> - <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font> - <font weight="700" style="normal">SourceSansPro-Bold.ttf</font> - <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font> - </family> - <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/> - - <family name="roboto-flex"> - <font supportedAxes="wght">RobotoFlex-Regular.ttf - <axis tag="wdth" stylevalue="100" /> - </font> - </family> - - <!-- fallback fonts --> - <family lang="und-Arab" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoNaskhArabic"> - NotoNaskhArabic-Regular.ttf - </font> - <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font> - </family> - <family lang="und-Arab" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI"> - NotoNaskhArabicUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font> - </family> - <family lang="und-Ethi"> - <font postScriptName="NotoSansEthiopic-Regular" supportedAxes="wght"> - NotoSansEthiopic-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifEthiopic-Regular" supportedAxes="wght"> - NotoSerifEthiopic-VF.ttf - </font> - </family> - <family lang="und-Hebr"> - <font weight="400" style="normal" postScriptName="NotoSansHebrew"> - NotoSansHebrew-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font> - </family> - <family lang="und-Thai" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThai-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif"> - NotoSerifThai-Regular.ttf - </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font> - </family> - <family lang="und-Thai" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansThaiUI"> - NotoSansThaiUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font> - </family> - <family lang="und-Armn"> - <font postScriptName="NotoSansArmenian-Regular" supportedAxes="wght"> - NotoSansArmenian-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifArmenian-Regular" supportedAxes="wght"> - NotoSerifArmenian-VF.ttf - </font> - </family> - <family lang="und-Geor,und-Geok"> - <font postScriptName="NotoSansGeorgian-Regular" supportedAxes="wght"> - NotoSansGeorgian-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifGeorgian-Regular" supportedAxes="wght"> - NotoSerifGeorgian-VF.ttf - </font> - </family> - <family lang="und-Deva" variant="elegant"> - <font postScriptName="NotoSansDevanagari-Regular" supportedAxes="wght"> - NotoSansDevanagari-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifDevanagari-Regular" supportedAxes="wght"> - NotoSerifDevanagari-VF.ttf - </font> - </family> - <family lang="und-Deva" variant="compact"> - <font postScriptName="NotoSansDevanagariUI-Regular" supportedAxes="wght"> - NotoSansDevanagariUI-VF.ttf - </font> - </family> - - <!-- All scripts of India should come after Devanagari, due to shared - danda characters. - --> - <family lang="und-Gujr" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansGujarati"> - NotoSansGujarati-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font> - <font style="normal" fallbackFor="serif" postScriptName="NotoSerifGujarati-Regular" - supportedAxes="wght"> - NotoSerifGujarati-VF.ttf - </font> - </family> - <family lang="und-Gujr" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI"> - NotoSansGujaratiUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font> - </family> - <family lang="und-Guru" variant="elegant"> - <font postScriptName="NotoSansGurmukhi-Regular" supportedAxes="wght"> - NotoSansGurmukhi-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifGurmukhi-Regular" supportedAxes="wght"> - NotoSerifGurmukhi-VF.ttf - </font> - </family> - <family lang="und-Guru" variant="compact"> - <font postScriptName="NotoSansGurmukhiUI-Regular" supportedAxes="wght"> - NotoSansGurmukhiUI-VF.ttf - </font> - </family> - <family lang="und-Taml" variant="elegant"> - <font postScriptName="NotoSansTamil-Regular" supportedAxes="wght"> - NotoSansTamil-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifTamil-Regular" supportedAxes="wght"> - NotoSerifTamil-VF.ttf - </font> - </family> - <family lang="und-Taml" variant="compact"> - <font postScriptName="NotoSansTamilUI-Regular" supportedAxes="wght"> - NotoSansTamilUI-VF.ttf - </font> - </family> - <family lang="und-Mlym" variant="elegant"> - <font postScriptName="NotoSansMalayalam-Regular" supportedAxes="wght"> - NotoSansMalayalam-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifMalayalam-Regular" supportedAxes="wght"> - NotoSerifMalayalam-VF.ttf - </font> - </family> - <family lang="und-Mlym" variant="compact"> - <font postScriptName="NotoSansMalayalamUI-Regular" supportedAxes="wght"> - NotoSansMalayalamUI-VF.ttf - </font> - </family> - <family lang="und-Beng" variant="elegant"> - <font postScriptName="NotoSansBengali-Regular" supportedAxes="wght"> - NotoSansBengali-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular" supportedAxes="wght"> - NotoSerifBengali-VF.ttf - </font> - </family> - <family lang="und-Beng" variant="compact"> - <font postScriptName="NotoSansBengaliUI-Regular" supportedAxes="wght"> - NotoSansBengaliUI-VF.ttf - </font> - </family> - <family lang="und-Telu" variant="elegant"> - <font postScriptName="NotoSansTelugu-Regular" supportedAxes="wght"> - NotoSansTelugu-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifTelugu-Regular" supportedAxes="wght"> - NotoSerifTelugu-VF.ttf - </font> - </family> - <family lang="und-Telu" variant="compact"> - <font postScriptName="NotoSansTeluguUI-Regular" supportedAxes="wght"> - NotoSansTeluguUI-VF.ttf - </font> - </family> - <family lang="und-Knda" variant="elegant"> - <font postScriptName="NotoSansKannada-Regular" supportedAxes="wght"> - NotoSansKannada-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifKannada-Regular" supportedAxes="wght"> - NotoSerifKannada-VF.ttf - </font> - </family> - <family lang="und-Knda" variant="compact"> - <font postScriptName="NotoSansKannadaUI-Regular" supportedAxes="wght"> - NotoSansKannadaUI-VF.ttf - </font> - </family> - <family lang="und-Orya" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font> - </family> - <family lang="und-Orya" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansOriyaUI"> - NotoSansOriyaUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font> - </family> - <family lang="und-Sinh" variant="elegant"> - <font postScriptName="NotoSansSinhala-Regular" supportedAxes="wght"> - NotoSansSinhala-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular" supportedAxes="wght"> - NotoSerifSinhala-VF.ttf - </font> - </family> - <family lang="und-Sinh" variant="compact"> - <font postScriptName="NotoSansSinhalaUI-Regular" supportedAxes="wght"> - NotoSansSinhalaUI-VF.ttf - </font> - </family> - <!-- TODO: NotoSansKhmer uses non-standard wght value, so cannot use auto-adjustment. --> - <family lang="und-Khmr" variant="elegant"> - <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="26.0"/> - </font> - <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="39.0"/> - </font> - <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="58.0"/> - </font> - <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="90.0"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="108.0"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="128.0"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="151.0"/> - </font> - <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="169.0"/> - </font> - <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="190.0"/> - </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font> - </family> - <family lang="und-Khmr" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansKhmerUI"> - NotoSansKhmerUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font> - </family> - <family lang="und-Laoo" variant="elegant"> - <font weight="400" style="normal">NotoSansLao-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansLao-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif"> - NotoSerifLao-Regular.ttf - </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> - </family> - <family lang="und-Laoo" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> - </family> - <family lang="und-Mymr" variant="elegant"> - <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font> - <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font> - <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font> - </family> - <family lang="und-Mymr" variant="compact"> - <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font> - <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font> - <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font> - </family> - <family lang="und-Thaa"> - <font weight="400" style="normal" postScriptName="NotoSansThaana"> - NotoSansThaana-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font> - </family> - <family lang="und-Cham"> - <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansCham-Bold.ttf</font> - </family> - <family lang="und-Ahom"> - <font weight="400" style="normal">NotoSansAhom-Regular.otf</font> - </family> - <family lang="und-Adlm"> - <font postScriptName="NotoSansAdlam-Regular" supportedAxes="wght"> - NotoSansAdlam-VF.ttf - </font> - </family> - <family lang="und-Avst"> - <font weight="400" style="normal" postScriptName="NotoSansAvestan"> - NotoSansAvestan-Regular.ttf - </font> - </family> - <family lang="und-Bali"> - <font weight="400" style="normal" postScriptName="NotoSansBalinese"> - NotoSansBalinese-Regular.ttf - </font> - </family> - <family lang="und-Bamu"> - <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf - </font> - </family> - <family lang="und-Batk"> - <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf - </font> - </family> - <family lang="und-Brah"> - <font weight="400" style="normal" postScriptName="NotoSansBrahmi"> - NotoSansBrahmi-Regular.ttf - </font> - </family> - <family lang="und-Bugi"> - <font weight="400" style="normal" postScriptName="NotoSansBuginese"> - NotoSansBuginese-Regular.ttf - </font> - </family> - <family lang="und-Buhd"> - <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf - </font> - </family> - <family lang="und-Cans"> - <font weight="400" style="normal"> - NotoSansCanadianAboriginal-Regular.ttf - </font> - </family> - <family lang="und-Cari"> - <font weight="400" style="normal" postScriptName="NotoSansCarian"> - NotoSansCarian-Regular.ttf - </font> - </family> - <family lang="und-Cakm"> - <font weight="400" style="normal">NotoSansChakma-Regular.otf</font> - </family> - <family lang="und-Cher"> - <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font> - </family> - <family lang="und-Copt"> - <font weight="400" style="normal" postScriptName="NotoSansCoptic"> - NotoSansCoptic-Regular.ttf - </font> - </family> - <family lang="und-Xsux"> - <font weight="400" style="normal" postScriptName="NotoSansCuneiform"> - NotoSansCuneiform-Regular.ttf - </font> - </family> - <family lang="und-Cprt"> - <font weight="400" style="normal" postScriptName="NotoSansCypriot"> - NotoSansCypriot-Regular.ttf - </font> - </family> - <family lang="und-Dsrt"> - <font weight="400" style="normal" postScriptName="NotoSansDeseret"> - NotoSansDeseret-Regular.ttf - </font> - </family> - <family lang="und-Egyp"> - <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs"> - NotoSansEgyptianHieroglyphs-Regular.ttf - </font> - </family> - <family lang="und-Elba"> - <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font> - </family> - <family lang="und-Glag"> - <font weight="400" style="normal" postScriptName="NotoSansGlagolitic"> - NotoSansGlagolitic-Regular.ttf - </font> - </family> - <family lang="und-Goth"> - <font weight="400" style="normal" postScriptName="NotoSansGothic"> - NotoSansGothic-Regular.ttf - </font> - </family> - <family lang="und-Hano"> - <font weight="400" style="normal" postScriptName="NotoSansHanunoo"> - NotoSansHanunoo-Regular.ttf - </font> - </family> - <family lang="und-Armi"> - <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic"> - NotoSansImperialAramaic-Regular.ttf - </font> - </family> - <family lang="und-Phli"> - <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi"> - NotoSansInscriptionalPahlavi-Regular.ttf - </font> - </family> - <family lang="und-Prti"> - <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian"> - NotoSansInscriptionalParthian-Regular.ttf - </font> - </family> - <family lang="und-Java"> - <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font> - </family> - <family lang="und-Kthi"> - <font weight="400" style="normal" postScriptName="NotoSansKaithi"> - NotoSansKaithi-Regular.ttf - </font> - </family> - <family lang="und-Kali"> - <font weight="400" style="normal" postScriptName="NotoSansKayahLi"> - NotoSansKayahLi-Regular.ttf - </font> - </family> - <family lang="und-Khar"> - <font weight="400" style="normal" postScriptName="NotoSansKharoshthi"> - NotoSansKharoshthi-Regular.ttf - </font> - </family> - <family lang="und-Lepc"> - <font weight="400" style="normal" postScriptName="NotoSansLepcha"> - NotoSansLepcha-Regular.ttf - </font> - </family> - <family lang="und-Limb"> - <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf - </font> - </family> - <family lang="und-Linb"> - <font weight="400" style="normal" postScriptName="NotoSansLinearB"> - NotoSansLinearB-Regular.ttf - </font> - </family> - <family lang="und-Lisu"> - <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf - </font> - </family> - <family lang="und-Lyci"> - <font weight="400" style="normal" postScriptName="NotoSansLycian"> - NotoSansLycian-Regular.ttf - </font> - </family> - <family lang="und-Lydi"> - <font weight="400" style="normal" postScriptName="NotoSansLydian"> - NotoSansLydian-Regular.ttf - </font> - </family> - <family lang="und-Mand"> - <font weight="400" style="normal" postScriptName="NotoSansMandaic"> - NotoSansMandaic-Regular.ttf - </font> - </family> - <family lang="und-Mtei"> - <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek"> - NotoSansMeeteiMayek-Regular.ttf - </font> - </family> - <family lang="und-Talu"> - <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue"> - NotoSansNewTaiLue-Regular.ttf - </font> - </family> - <family lang="und-Nkoo"> - <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf - </font> - </family> - <family lang="und-Ogam"> - <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf - </font> - </family> - <family lang="und-Olck"> - <font weight="400" style="normal" postScriptName="NotoSansOlChiki"> - NotoSansOlChiki-Regular.ttf - </font> - </family> - <family lang="und-Ital"> - <font weight="400" style="normal" postScriptName="NotoSansOldItalic"> - NotoSansOldItalic-Regular.ttf - </font> - </family> - <family lang="und-Xpeo"> - <font weight="400" style="normal" postScriptName="NotoSansOldPersian"> - NotoSansOldPersian-Regular.ttf - </font> - </family> - <family lang="und-Sarb"> - <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian"> - NotoSansOldSouthArabian-Regular.ttf - </font> - </family> - <family lang="und-Orkh"> - <font weight="400" style="normal" postScriptName="NotoSansOldTurkic"> - NotoSansOldTurkic-Regular.ttf - </font> - </family> - <family lang="und-Osge"> - <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font> - </family> - <family lang="und-Osma"> - <font weight="400" style="normal" postScriptName="NotoSansOsmanya"> - NotoSansOsmanya-Regular.ttf - </font> - </family> - <family lang="und-Phnx"> - <font weight="400" style="normal" postScriptName="NotoSansPhoenician"> - NotoSansPhoenician-Regular.ttf - </font> - </family> - <family lang="und-Rjng"> - <font weight="400" style="normal" postScriptName="NotoSansRejang"> - NotoSansRejang-Regular.ttf - </font> - </family> - <family lang="und-Runr"> - <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf - </font> - </family> - <family lang="und-Samr"> - <font weight="400" style="normal" postScriptName="NotoSansSamaritan"> - NotoSansSamaritan-Regular.ttf - </font> - </family> - <family lang="und-Saur"> - <font weight="400" style="normal" postScriptName="NotoSansSaurashtra"> - NotoSansSaurashtra-Regular.ttf - </font> - </family> - <family lang="und-Shaw"> - <font weight="400" style="normal" postScriptName="NotoSansShavian"> - NotoSansShavian-Regular.ttf - </font> - </family> - <family lang="und-Sund"> - <font weight="400" style="normal" postScriptName="NotoSansSundanese"> - NotoSansSundanese-Regular.ttf - </font> - </family> - <family lang="und-Sylo"> - <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri"> - NotoSansSylotiNagri-Regular.ttf - </font> - </family> - <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. --> - <family lang="und-Syre"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela"> - NotoSansSyriacEstrangela-Regular.ttf - </font> - </family> - <family lang="und-Syrn"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern"> - NotoSansSyriacEastern-Regular.ttf - </font> - </family> - <family lang="und-Syrj"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern"> - NotoSansSyriacWestern-Regular.ttf - </font> - </family> - <family lang="und-Tglg"> - <font weight="400" style="normal" postScriptName="NotoSansTagalog"> - NotoSansTagalog-Regular.ttf - </font> - </family> - <family lang="und-Tagb"> - <font weight="400" style="normal" postScriptName="NotoSansTagbanwa"> - NotoSansTagbanwa-Regular.ttf - </font> - </family> - <family lang="und-Lana"> - <font weight="400" style="normal" postScriptName="NotoSansTaiTham"> - NotoSansTaiTham-Regular.ttf - </font> - </family> - <family lang="und-Tavt"> - <font weight="400" style="normal" postScriptName="NotoSansTaiViet"> - NotoSansTaiViet-Regular.ttf - </font> - </family> - <family lang="und-Tibt"> - <font postScriptName="NotoSerifTibetan-Regular" supportedAxes="wght"> - NotoSerifTibetan-VF.ttf - </font> - </family> - <family lang="und-Tfng"> - <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font> - </family> - <family lang="und-Ugar"> - <font weight="400" style="normal" postScriptName="NotoSansUgaritic"> - NotoSansUgaritic-Regular.ttf - </font> - </family> - <family lang="und-Vaii"> - <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf - </font> - </family> - <family> - <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> - </family> - <family lang="zh-Hans"> - <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Regular"> - NotoSansCJK-Regular.ttc - </font> - <font weight="400" style="normal" index="2" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="zh-Hant,zh-Bopo"> - <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Regular"> - NotoSansCJK-Regular.ttc - </font> - <font weight="400" style="normal" index="3" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="ja"> - <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Regular"> - NotoSansCJK-Regular.ttc - </font> - <font weight="400" style="normal" index="0" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="ja"> - <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght"> - NotoSerifHentaigana.ttf - <axis tag="wght" stylevalue="400"/> - </font> - </family> - <family lang="ko"> - <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular"> - NotoSansCJK-Regular.ttc - </font> - <font weight="400" style="normal" index="1" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="und-Zsye"> - <font weight="400" style="normal">NotoColorEmoji.ttf</font> - </family> - <family lang="und-Zsye"> - <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font> - </family> - <family lang="und-Zsym"> - <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font> - </family> - <!-- - Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't - override the East Asian punctuation for Chinese. - --> - <family lang="und-Tale"> - <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf - </font> - </family> - <family lang="und-Yiii"> - <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font> - </family> - <family lang="und-Mong"> - <font weight="400" style="normal" postScriptName="NotoSansMongolian"> - NotoSansMongolian-Regular.ttf - </font> - </family> - <family lang="und-Phag"> - <font weight="400" style="normal" postScriptName="NotoSansPhagsPa"> - NotoSansPhagsPa-Regular.ttf - </font> - </family> - <family lang="und-Hluw"> - <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font> - </family> - <family lang="und-Bass"> - <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font> - </family> - <family lang="und-Bhks"> - <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font> - </family> - <family lang="und-Hatr"> - <font weight="400" style="normal">NotoSansHatran-Regular.otf</font> - </family> - <family lang="und-Lina"> - <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font> - </family> - <family lang="und-Mani"> - <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font> - </family> - <family lang="und-Marc"> - <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font> - </family> - <family lang="und-Merc"> - <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font> - </family> - <family lang="und-Plrd"> - <font weight="400" style="normal">NotoSansMiao-Regular.otf</font> - </family> - <family lang="und-Mroo"> - <font weight="400" style="normal">NotoSansMro-Regular.otf</font> - </family> - <family lang="und-Mult"> - <font weight="400" style="normal">NotoSansMultani-Regular.otf</font> - </family> - <family lang="und-Nbat"> - <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font> - </family> - <family lang="und-Newa"> - <font weight="400" style="normal">NotoSansNewa-Regular.otf</font> - </family> - <family lang="und-Narb"> - <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font> - </family> - <family lang="und-Perm"> - <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font> - </family> - <family lang="und-Hmng"> - <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font> - </family> - <family lang="und-Palm"> - <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font> - </family> - <family lang="und-Pauc"> - <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font> - </family> - <family lang="und-Shrd"> - <font weight="400" style="normal">NotoSansSharada-Regular.otf</font> - </family> - <family lang="und-Sora"> - <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font> - </family> - <family lang="und-Gong"> - <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font> - </family> - <family lang="und-Rohg"> - <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font> - </family> - <family lang="und-Khoj"> - <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font> - </family> - <family lang="und-Gonm"> - <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font> - </family> - <family lang="und-Wcho"> - <font weight="400" style="normal">NotoSansWancho-Regular.otf</font> - </family> - <family lang="und-Wara"> - <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font> - </family> - <family lang="und-Gran"> - <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font> - </family> - <family lang="und-Modi"> - <font weight="400" style="normal">NotoSansModi-Regular.ttf</font> - </family> - <family lang="und-Dogr"> - <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font> - </family> - <family lang="und-Medf"> - <font postScriptName="NotoSansMedefaidrin-Regular" supportedAxes="wght"> - NotoSansMedefaidrin-VF.ttf - </font> - </family> - <family lang="und-Soyo"> - <font postScriptName="NotoSansSoyombo-Regular" supportedAxes="wght"> - NotoSansSoyombo-VF.ttf - </font> - </family> - <family lang="und-Takr"> - <font postScriptName="NotoSansTakri-Regular" supportedAxes="wght"> - NotoSansTakri-VF.ttf - </font> - </family> - <family lang="und-Hmnp"> - <font postScriptName="NotoSerifHmongNyiakeng-Regular" supportedAxes="wght"> - NotoSerifNyiakengPuachueHmong-VF.ttf - </font> - </family> - <family lang="und-Yezi"> - <font postScriptName="NotoSerifYezidi-Regular" supportedAxes="wght"> - NotoSerifYezidi-VF.ttf - </font> - </family> -</familyset> diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml deleted file mode 100644 index 407d70410bbc..000000000000 --- a/data/fonts/font_fallback_cjkvf.xml +++ /dev/null @@ -1,966 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - In this file, all fonts without names are added to the default list. - Fonts are chosen based on a match: full BCP-47 language tag including - script, then just language, and finally order (the first font containing - the glyph). - - Order of appearance is also the tiebreaker for weight matching. This is - the reason why the 900 weights of Roboto precede the 700 weights - we - prefer the former when an 800 weight is requested. Since bold spans - effectively add 300 to the weight, this ensures that 900 is the bold - paired with the 500 weight, ensuring adequate contrast. - - - The font_fallback.xml defines the list of font used by the system. - - `familyset` node: - A `familyset` element must be a root node of the font_fallback.xml. No attributes are allowed - to `familyset` node. - The `familyset` node can contains `family` and `alias` nodes. Any other nodes will be ignored. - - `family` node: - A `family` node defines a single font family definition. - A font family is a set of fonts for drawing text in various styles such as weight, slant. - There are three types of families, default family, named family and locale fallback family. - - The default family is a special family node appeared the first node of the `familyset` node. - The default family is used as first priority fallback. - Only `name` attribute can be used for default family node. If the `name` attribute is - specified, This family will also works as named family. - - The named family is a family that has name attribute. The named family defines a new fallback. - For example, if the name attribute is "serif", it creates serif fallback. Developers can - access the fallback by using Typeface#create API. - The named family can not have attribute other than `name` attribute. The `name` attribute - cannot be empty. - - The locale fallback family is a font family that is used for fallback. The fallback family is - used when the named family or default family cannot be used. The locale fallback family can - have `lang` attribute and `variant` attribute. The `lang` attribute is an optional comma - separated BCP-47i language tag. The `variant` is an optional attribute that can be one one - `element`, `compact`. If a `variant` attribute is not specified, it is treated as default. - - `alias` node: - An `alias` node defines a alias of named family with changing weight offset. An `alias` node - can have mandatory `name` and `to` attribute and optional `weight` attribute. This `alias` - defines new fallback that has the name of specified `name` attribute. The fallback list is - the same to the fallback that of the name specified with `to` attribute. If `weight` attribute - is specified, the base weight offset is shifted to the specified value. For example, if the - `weight` is 500, the output text is drawn with 500 of weight. - - `font` node: - A `font` node defines a single font definition. There are two types of fonts, static font and - variable font. - - A static font can have `weight`, `style`, `index` and `postScriptName` attributes. A `weight` - is a mandatory attribute that defines the weight of the font. Any number between 0 to 1000 is - valid. A `style` is a mandatory attribute that defines the style of the font. A 'style' - attribute can be `normal` or `italic`. An `index` is an optional attribute that defines the - index of the font collection. If this is not specified, it is treated as 0. If the font file - is not a font collection, this attribute is ignored. A `postScriptName` attribute is an - optional attribute. A PostScript name is used for identifying target of system font update. - If this is not specified, the system assumes the filename is same to PostScript name of the - font file. For example, if the font file is "Roboto-Regular.ttf", the system assume the - PostScript name of this font is "Roboto-Regular". - - A variable font can be only defined for the variable font file. A variable font can have - `axis` child nodes for specifying axis values. A variable font can have all attribute of - static font and can have additional `supportedAxes` attribute. A `supportedAxes` attribute - is a comma separated supported axis tags. As of Android V, only `wght` and `ital` axes can - be specified. - - If `supportedAxes` attribute is not specified, this `font` node works as static font of the - single instance of variable font specified with `axis` children. - - If `supportedAxes` attribute is specified, the system dynamically create font instance for the - given weight and style value. If `wght` is specified in `supportedAxes` attribute the `weight` - attribute and `axis` child that has `wght` tag become optional and ignored because it is - determined by system at runtime. Similarly, if `ital` is specified in `supportedAxes` - attribute, the `style` attribute and `axis` child that has `ital` tag become optional and - ignored. - - `axis` node: - An `axis` node defines a font variation value for a tag. An `axis` node can have two mandatory - attributes, `tag` and `value`. If the font is variable font and the same tag `axis` node is - specified in `supportedAxes` attribute, the style value works like a default instance. ---> -<familyset> - <!-- first font is default --> - <family name="sans-serif"> - <font supportedAxes="wght,ital">Roboto-Regular.ttf - <axis tag="wdth" stylevalue="100" /> - </font> - </family> - - - <!-- Note that aliases must come after the fonts they reference. --> - <alias name="sans-serif-thin" to="sans-serif" weight="100" /> - <alias name="sans-serif-light" to="sans-serif" weight="300" /> - <alias name="sans-serif-medium" to="sans-serif" weight="500" /> - <alias name="sans-serif-black" to="sans-serif" weight="900" /> - <alias name="arial" to="sans-serif" /> - <alias name="helvetica" to="sans-serif" /> - <alias name="tahoma" to="sans-serif" /> - <alias name="verdana" to="sans-serif" /> - - <family name="sans-serif-condensed"> - <font supportedAxes="wght,ital">Roboto-Regular.ttf - <axis tag="wdth" stylevalue="75" /> - </font> - </family> - <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" /> - <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" /> - - <family name="serif"> - <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font> - <font weight="700" style="normal">NotoSerif-Bold.ttf</font> - <font weight="400" style="italic">NotoSerif-Italic.ttf</font> - <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font> - </family> - <alias name="serif-bold" to="serif" weight="700" /> - <alias name="times" to="serif" /> - <alias name="times new roman" to="serif" /> - <alias name="palatino" to="serif" /> - <alias name="georgia" to="serif" /> - <alias name="baskerville" to="serif" /> - <alias name="goudy" to="serif" /> - <alias name="fantasy" to="serif" /> - <alias name="ITC Stone Serif" to="serif" /> - - <family name="monospace"> - <font weight="400" style="normal">DroidSansMono.ttf</font> - </family> - <alias name="sans-serif-monospace" to="monospace" /> - <alias name="monaco" to="monospace" /> - - <family name="serif-monospace"> - <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font> - </family> - <alias name="courier" to="serif-monospace" /> - <alias name="courier new" to="serif-monospace" /> - - <family name="casual"> - <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font> - </family> - - <family name="cursive"> - <font supportedAxes="wght">DancingScript-Regular.ttf</font> - </family> - - <family name="sans-serif-smallcaps"> - <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font> - </family> - - <family name="source-sans-pro"> - <font weight="400" style="normal">SourceSansPro-Regular.ttf</font> - <font weight="400" style="italic">SourceSansPro-Italic.ttf</font> - <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font> - <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font> - <font weight="700" style="normal">SourceSansPro-Bold.ttf</font> - <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font> - </family> - <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/> - - <family name="roboto-flex"> - <font supportedAxes="wght">RobotoFlex-Regular.ttf - <axis tag="wdth" stylevalue="100" /> - </font> - </family> - - <!-- fallback fonts --> - <family lang="und-Arab" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoNaskhArabic"> - NotoNaskhArabic-Regular.ttf - </font> - <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font> - </family> - <family lang="und-Arab" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI"> - NotoNaskhArabicUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font> - </family> - <family lang="und-Ethi"> - <font postScriptName="NotoSansEthiopic-Regular" supportedAxes="wght"> - NotoSansEthiopic-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifEthiopic-Regular" supportedAxes="wght"> - NotoSerifEthiopic-VF.ttf - </font> - </family> - <family lang="und-Hebr"> - <font weight="400" style="normal" postScriptName="NotoSansHebrew"> - NotoSansHebrew-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font> - </family> - <family lang="und-Thai" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThai-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif"> - NotoSerifThai-Regular.ttf - </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font> - </family> - <family lang="und-Thai" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansThaiUI"> - NotoSansThaiUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font> - </family> - <family lang="und-Armn"> - <font postScriptName="NotoSansArmenian-Regular" supportedAxes="wght"> - NotoSansArmenian-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifArmenian-Regular" supportedAxes="wght"> - NotoSerifArmenian-VF.ttf - </font> - </family> - <family lang="und-Geor,und-Geok"> - <font postScriptName="NotoSansGeorgian-Regular" supportedAxes="wght"> - NotoSansGeorgian-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifGeorgian-Regular" supportedAxes="wght"> - NotoSerifGeorgian-VF.ttf - </font> - </family> - <family lang="und-Deva" variant="elegant"> - <font postScriptName="NotoSansDevanagari-Regular" supportedAxes="wght"> - NotoSansDevanagari-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifDevanagari-Regular" supportedAxes="wght"> - NotoSerifDevanagari-VF.ttf - </font> - </family> - <family lang="und-Deva" variant="compact"> - <font postScriptName="NotoSansDevanagariUI-Regular" supportedAxes="wght"> - NotoSansDevanagariUI-VF.ttf - </font> - </family> - - <!-- All scripts of India should come after Devanagari, due to shared - danda characters. - --> - <family lang="und-Gujr" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansGujarati"> - NotoSansGujarati-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font> - <font style="normal" fallbackFor="serif" postScriptName="NotoSerifGujarati-Regular" - supportedAxes="wght"> - NotoSerifGujarati-VF.ttf - </font> - </family> - <family lang="und-Gujr" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI"> - NotoSansGujaratiUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font> - </family> - <family lang="und-Guru" variant="elegant"> - <font postScriptName="NotoSansGurmukhi-Regular" supportedAxes="wght"> - NotoSansGurmukhi-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifGurmukhi-Regular" supportedAxes="wght"> - NotoSerifGurmukhi-VF.ttf - </font> - </family> - <family lang="und-Guru" variant="compact"> - <font postScriptName="NotoSansGurmukhiUI-Regular" supportedAxes="wght"> - NotoSansGurmukhiUI-VF.ttf - </font> - </family> - <family lang="und-Taml" variant="elegant"> - <font postScriptName="NotoSansTamil-Regular" supportedAxes="wght"> - NotoSansTamil-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifTamil-Regular" supportedAxes="wght"> - NotoSerifTamil-VF.ttf - </font> - </family> - <family lang="und-Taml" variant="compact"> - <font postScriptName="NotoSansTamilUI-Regular" supportedAxes="wght"> - NotoSansTamilUI-VF.ttf - </font> - </family> - <family lang="und-Mlym" variant="elegant"> - <font postScriptName="NotoSansMalayalam-Regular" supportedAxes="wght"> - NotoSansMalayalam-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifMalayalam-Regular" supportedAxes="wght"> - NotoSerifMalayalam-VF.ttf - </font> - </family> - <family lang="und-Mlym" variant="compact"> - <font postScriptName="NotoSansMalayalamUI-Regular" supportedAxes="wght"> - NotoSansMalayalamUI-VF.ttf - </font> - </family> - <family lang="und-Beng" variant="elegant"> - <font postScriptName="NotoSansBengali-Regular" supportedAxes="wght"> - NotoSansBengali-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular" supportedAxes="wght"> - NotoSerifBengali-VF.ttf - </font> - </family> - <family lang="und-Beng" variant="compact"> - <font postScriptName="NotoSansBengaliUI-Regular" supportedAxes="wght"> - NotoSansBengaliUI-VF.ttf - </font> - </family> - <family lang="und-Telu" variant="elegant"> - <font postScriptName="NotoSansTelugu-Regular" supportedAxes="wght"> - NotoSansTelugu-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifTelugu-Regular" supportedAxes="wght"> - NotoSerifTelugu-VF.ttf - </font> - </family> - <family lang="und-Telu" variant="compact"> - <font postScriptName="NotoSansTeluguUI-Regular" supportedAxes="wght"> - NotoSansTeluguUI-VF.ttf - </font> - </family> - <family lang="und-Knda" variant="elegant"> - <font postScriptName="NotoSansKannada-Regular" supportedAxes="wght"> - NotoSansKannada-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifKannada-Regular" supportedAxes="wght"> - NotoSerifKannada-VF.ttf - </font> - </family> - <family lang="und-Knda" variant="compact"> - <font postScriptName="NotoSansKannadaUI-Regular" supportedAxes="wght"> - NotoSansKannadaUI-VF.ttf - </font> - </family> - <family lang="und-Orya" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font> - </family> - <family lang="und-Orya" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansOriyaUI"> - NotoSansOriyaUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font> - </family> - <family lang="und-Sinh" variant="elegant"> - <font postScriptName="NotoSansSinhala-Regular" supportedAxes="wght"> - NotoSansSinhala-VF.ttf - </font> - <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular" supportedAxes="wght"> - NotoSerifSinhala-VF.ttf - </font> - </family> - <family lang="und-Sinh" variant="compact"> - <font postScriptName="NotoSansSinhalaUI-Regular" supportedAxes="wght"> - NotoSansSinhalaUI-VF.ttf - </font> - </family> - <!-- TODO: NotoSansKhmer uses non-standard wght value, so cannot use auto-adjustment. --> - <family lang="und-Khmr" variant="elegant"> - <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="26.0"/> - </font> - <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="39.0"/> - </font> - <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="58.0"/> - </font> - <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="90.0"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="108.0"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="128.0"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="151.0"/> - </font> - <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="169.0"/> - </font> - <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="190.0"/> - </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font> - </family> - <family lang="und-Khmr" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansKhmerUI"> - NotoSansKhmerUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font> - </family> - <family lang="und-Laoo" variant="elegant"> - <font weight="400" style="normal">NotoSansLao-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansLao-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif"> - NotoSerifLao-Regular.ttf - </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> - </family> - <family lang="und-Laoo" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> - </family> - <family lang="und-Mymr" variant="elegant"> - <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font> - <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font> - <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font> - </family> - <family lang="und-Mymr" variant="compact"> - <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font> - <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font> - <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font> - </family> - <family lang="und-Thaa"> - <font weight="400" style="normal" postScriptName="NotoSansThaana"> - NotoSansThaana-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font> - </family> - <family lang="und-Cham"> - <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansCham-Bold.ttf</font> - </family> - <family lang="und-Ahom"> - <font weight="400" style="normal">NotoSansAhom-Regular.otf</font> - </family> - <family lang="und-Adlm"> - <font postScriptName="NotoSansAdlam-Regular" supportedAxes="wght"> - NotoSansAdlam-VF.ttf - </font> - </family> - <family lang="und-Avst"> - <font weight="400" style="normal" postScriptName="NotoSansAvestan"> - NotoSansAvestan-Regular.ttf - </font> - </family> - <family lang="und-Bali"> - <font weight="400" style="normal" postScriptName="NotoSansBalinese"> - NotoSansBalinese-Regular.ttf - </font> - </family> - <family lang="und-Bamu"> - <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf - </font> - </family> - <family lang="und-Batk"> - <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf - </font> - </family> - <family lang="und-Brah"> - <font weight="400" style="normal" postScriptName="NotoSansBrahmi"> - NotoSansBrahmi-Regular.ttf - </font> - </family> - <family lang="und-Bugi"> - <font weight="400" style="normal" postScriptName="NotoSansBuginese"> - NotoSansBuginese-Regular.ttf - </font> - </family> - <family lang="und-Buhd"> - <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf - </font> - </family> - <family lang="und-Cans"> - <font weight="400" style="normal"> - NotoSansCanadianAboriginal-Regular.ttf - </font> - </family> - <family lang="und-Cari"> - <font weight="400" style="normal" postScriptName="NotoSansCarian"> - NotoSansCarian-Regular.ttf - </font> - </family> - <family lang="und-Cakm"> - <font weight="400" style="normal">NotoSansChakma-Regular.otf</font> - </family> - <family lang="und-Cher"> - <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font> - </family> - <family lang="und-Copt"> - <font weight="400" style="normal" postScriptName="NotoSansCoptic"> - NotoSansCoptic-Regular.ttf - </font> - </family> - <family lang="und-Xsux"> - <font weight="400" style="normal" postScriptName="NotoSansCuneiform"> - NotoSansCuneiform-Regular.ttf - </font> - </family> - <family lang="und-Cprt"> - <font weight="400" style="normal" postScriptName="NotoSansCypriot"> - NotoSansCypriot-Regular.ttf - </font> - </family> - <family lang="und-Dsrt"> - <font weight="400" style="normal" postScriptName="NotoSansDeseret"> - NotoSansDeseret-Regular.ttf - </font> - </family> - <family lang="und-Egyp"> - <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs"> - NotoSansEgyptianHieroglyphs-Regular.ttf - </font> - </family> - <family lang="und-Elba"> - <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font> - </family> - <family lang="und-Glag"> - <font weight="400" style="normal" postScriptName="NotoSansGlagolitic"> - NotoSansGlagolitic-Regular.ttf - </font> - </family> - <family lang="und-Goth"> - <font weight="400" style="normal" postScriptName="NotoSansGothic"> - NotoSansGothic-Regular.ttf - </font> - </family> - <family lang="und-Hano"> - <font weight="400" style="normal" postScriptName="NotoSansHanunoo"> - NotoSansHanunoo-Regular.ttf - </font> - </family> - <family lang="und-Armi"> - <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic"> - NotoSansImperialAramaic-Regular.ttf - </font> - </family> - <family lang="und-Phli"> - <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi"> - NotoSansInscriptionalPahlavi-Regular.ttf - </font> - </family> - <family lang="und-Prti"> - <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian"> - NotoSansInscriptionalParthian-Regular.ttf - </font> - </family> - <family lang="und-Java"> - <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font> - </family> - <family lang="und-Kthi"> - <font weight="400" style="normal" postScriptName="NotoSansKaithi"> - NotoSansKaithi-Regular.ttf - </font> - </family> - <family lang="und-Kali"> - <font weight="400" style="normal" postScriptName="NotoSansKayahLi"> - NotoSansKayahLi-Regular.ttf - </font> - </family> - <family lang="und-Khar"> - <font weight="400" style="normal" postScriptName="NotoSansKharoshthi"> - NotoSansKharoshthi-Regular.ttf - </font> - </family> - <family lang="und-Lepc"> - <font weight="400" style="normal" postScriptName="NotoSansLepcha"> - NotoSansLepcha-Regular.ttf - </font> - </family> - <family lang="und-Limb"> - <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf - </font> - </family> - <family lang="und-Linb"> - <font weight="400" style="normal" postScriptName="NotoSansLinearB"> - NotoSansLinearB-Regular.ttf - </font> - </family> - <family lang="und-Lisu"> - <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf - </font> - </family> - <family lang="und-Lyci"> - <font weight="400" style="normal" postScriptName="NotoSansLycian"> - NotoSansLycian-Regular.ttf - </font> - </family> - <family lang="und-Lydi"> - <font weight="400" style="normal" postScriptName="NotoSansLydian"> - NotoSansLydian-Regular.ttf - </font> - </family> - <family lang="und-Mand"> - <font weight="400" style="normal" postScriptName="NotoSansMandaic"> - NotoSansMandaic-Regular.ttf - </font> - </family> - <family lang="und-Mtei"> - <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek"> - NotoSansMeeteiMayek-Regular.ttf - </font> - </family> - <family lang="und-Talu"> - <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue"> - NotoSansNewTaiLue-Regular.ttf - </font> - </family> - <family lang="und-Nkoo"> - <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf - </font> - </family> - <family lang="und-Ogam"> - <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf - </font> - </family> - <family lang="und-Olck"> - <font weight="400" style="normal" postScriptName="NotoSansOlChiki"> - NotoSansOlChiki-Regular.ttf - </font> - </family> - <family lang="und-Ital"> - <font weight="400" style="normal" postScriptName="NotoSansOldItalic"> - NotoSansOldItalic-Regular.ttf - </font> - </family> - <family lang="und-Xpeo"> - <font weight="400" style="normal" postScriptName="NotoSansOldPersian"> - NotoSansOldPersian-Regular.ttf - </font> - </family> - <family lang="und-Sarb"> - <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian"> - NotoSansOldSouthArabian-Regular.ttf - </font> - </family> - <family lang="und-Orkh"> - <font weight="400" style="normal" postScriptName="NotoSansOldTurkic"> - NotoSansOldTurkic-Regular.ttf - </font> - </family> - <family lang="und-Osge"> - <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font> - </family> - <family lang="und-Osma"> - <font weight="400" style="normal" postScriptName="NotoSansOsmanya"> - NotoSansOsmanya-Regular.ttf - </font> - </family> - <family lang="und-Phnx"> - <font weight="400" style="normal" postScriptName="NotoSansPhoenician"> - NotoSansPhoenician-Regular.ttf - </font> - </family> - <family lang="und-Rjng"> - <font weight="400" style="normal" postScriptName="NotoSansRejang"> - NotoSansRejang-Regular.ttf - </font> - </family> - <family lang="und-Runr"> - <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf - </font> - </family> - <family lang="und-Samr"> - <font weight="400" style="normal" postScriptName="NotoSansSamaritan"> - NotoSansSamaritan-Regular.ttf - </font> - </family> - <family lang="und-Saur"> - <font weight="400" style="normal" postScriptName="NotoSansSaurashtra"> - NotoSansSaurashtra-Regular.ttf - </font> - </family> - <family lang="und-Shaw"> - <font weight="400" style="normal" postScriptName="NotoSansShavian"> - NotoSansShavian-Regular.ttf - </font> - </family> - <family lang="und-Sund"> - <font weight="400" style="normal" postScriptName="NotoSansSundanese"> - NotoSansSundanese-Regular.ttf - </font> - </family> - <family lang="und-Sylo"> - <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri"> - NotoSansSylotiNagri-Regular.ttf - </font> - </family> - <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. --> - <family lang="und-Syre"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela"> - NotoSansSyriacEstrangela-Regular.ttf - </font> - </family> - <family lang="und-Syrn"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern"> - NotoSansSyriacEastern-Regular.ttf - </font> - </family> - <family lang="und-Syrj"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern"> - NotoSansSyriacWestern-Regular.ttf - </font> - </family> - <family lang="und-Tglg"> - <font weight="400" style="normal" postScriptName="NotoSansTagalog"> - NotoSansTagalog-Regular.ttf - </font> - </family> - <family lang="und-Tagb"> - <font weight="400" style="normal" postScriptName="NotoSansTagbanwa"> - NotoSansTagbanwa-Regular.ttf - </font> - </family> - <family lang="und-Lana"> - <font weight="400" style="normal" postScriptName="NotoSansTaiTham"> - NotoSansTaiTham-Regular.ttf - </font> - </family> - <family lang="und-Tavt"> - <font weight="400" style="normal" postScriptName="NotoSansTaiViet"> - NotoSansTaiViet-Regular.ttf - </font> - </family> - <family lang="und-Tibt"> - <font postScriptName="NotoSerifTibetan-Regular" supportedAxes="wght"> - NotoSerifTibetan-VF.ttf - </font> - </family> - <family lang="und-Tfng"> - <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font> - </family> - <family lang="und-Ugar"> - <font weight="400" style="normal" postScriptName="NotoSansUgaritic"> - NotoSansUgaritic-Regular.ttf - </font> - </family> - <family lang="und-Vaii"> - <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf - </font> - </family> - <family> - <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> - </family> - <family lang="zh-Hans"> - <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular" - supportedAxes="wght"> - NotoSansCJK-Regular.ttc - <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400 - for making regular style as default. --> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="400" style="normal" index="2" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="zh-Hant,zh-Bopo"> - <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular" - supportedAxes="wght"> - NotoSansCJK-Regular.ttc - <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400 - for making regular style as default. --> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="400" style="normal" index="3" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="ja"> - <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular" - supportedAxes="wght"> - NotoSansCJK-Regular.ttc - <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400 - for making regular style as default. --> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="400" style="normal" index="0" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="ja"> - <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght"> - NotoSerifHentaigana.ttf - <axis tag="wght" stylevalue="400"/> - </font> - </family> - <family lang="ko"> - <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular" - supportedAxes="wght"> - NotoSansCJK-Regular.ttc - <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400 - for making regular style as default. --> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="400" style="normal" index="1" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="und-Zsye"> - <font weight="400" style="normal">NotoColorEmoji.ttf</font> - </family> - <family lang="und-Zsye"> - <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font> - </family> - <family lang="und-Zsym"> - <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font> - </family> - <!-- - Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't - override the East Asian punctuation for Chinese. - --> - <family lang="und-Tale"> - <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf - </font> - </family> - <family lang="und-Yiii"> - <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font> - </family> - <family lang="und-Mong"> - <font weight="400" style="normal" postScriptName="NotoSansMongolian"> - NotoSansMongolian-Regular.ttf - </font> - </family> - <family lang="und-Phag"> - <font weight="400" style="normal" postScriptName="NotoSansPhagsPa"> - NotoSansPhagsPa-Regular.ttf - </font> - </family> - <family lang="und-Hluw"> - <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font> - </family> - <family lang="und-Bass"> - <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font> - </family> - <family lang="und-Bhks"> - <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font> - </family> - <family lang="und-Hatr"> - <font weight="400" style="normal">NotoSansHatran-Regular.otf</font> - </family> - <family lang="und-Lina"> - <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font> - </family> - <family lang="und-Mani"> - <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font> - </family> - <family lang="und-Marc"> - <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font> - </family> - <family lang="und-Merc"> - <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font> - </family> - <family lang="und-Plrd"> - <font weight="400" style="normal">NotoSansMiao-Regular.otf</font> - </family> - <family lang="und-Mroo"> - <font weight="400" style="normal">NotoSansMro-Regular.otf</font> - </family> - <family lang="und-Mult"> - <font weight="400" style="normal">NotoSansMultani-Regular.otf</font> - </family> - <family lang="und-Nbat"> - <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font> - </family> - <family lang="und-Newa"> - <font weight="400" style="normal">NotoSansNewa-Regular.otf</font> - </family> - <family lang="und-Narb"> - <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font> - </family> - <family lang="und-Perm"> - <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font> - </family> - <family lang="und-Hmng"> - <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font> - </family> - <family lang="und-Palm"> - <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font> - </family> - <family lang="und-Pauc"> - <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font> - </family> - <family lang="und-Shrd"> - <font weight="400" style="normal">NotoSansSharada-Regular.otf</font> - </family> - <family lang="und-Sora"> - <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font> - </family> - <family lang="und-Gong"> - <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font> - </family> - <family lang="und-Rohg"> - <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font> - </family> - <family lang="und-Khoj"> - <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font> - </family> - <family lang="und-Gonm"> - <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font> - </family> - <family lang="und-Wcho"> - <font weight="400" style="normal">NotoSansWancho-Regular.otf</font> - </family> - <family lang="und-Wara"> - <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font> - </family> - <family lang="und-Gran"> - <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font> - </family> - <family lang="und-Modi"> - <font weight="400" style="normal">NotoSansModi-Regular.ttf</font> - </family> - <family lang="und-Dogr"> - <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font> - </family> - <family lang="und-Medf"> - <font postScriptName="NotoSansMedefaidrin-Regular" supportedAxes="wght"> - NotoSansMedefaidrin-VF.ttf - </font> - </family> - <family lang="und-Soyo"> - <font postScriptName="NotoSansSoyombo-Regular" supportedAxes="wght"> - NotoSansSoyombo-VF.ttf - </font> - </family> - <family lang="und-Takr"> - <font postScriptName="NotoSansTakri-Regular" supportedAxes="wght"> - NotoSansTakri-VF.ttf - </font> - </family> - <family lang="und-Hmnp"> - <font postScriptName="NotoSerifHmongNyiakeng-Regular" supportedAxes="wght"> - NotoSerifNyiakengPuachueHmong-VF.ttf - </font> - </family> - <family lang="und-Yezi"> - <font postScriptName="NotoSerifYezidi-Regular" supportedAxes="wght"> - NotoSerifYezidi-VF.ttf - </font> - </family> -</familyset> diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index d1aa8e9734c2..8cbc3000c250 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -1409,24 +1409,123 @@ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> </family> <family lang="zh-Hans"> - <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Regular"> + <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="100"/> + </font> + <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="200"/> + </font> + <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="300"/> + </font> + <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="800"/> + </font> + <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="900"/> </font> <font weight="400" style="normal" index="2" fallbackFor="serif" postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc </font> </family> <family lang="zh-Hant,zh-Bopo"> - <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Regular"> + <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="100"/> + </font> + <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="200"/> + </font> + <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="300"/> + </font> + <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="800"/> + </font> + <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="900"/> </font> <font weight="400" style="normal" index="3" fallbackFor="serif" postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc </font> </family> <family lang="ja"> - <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Regular"> + <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="100"/> + </font> + <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="200"/> + </font> + <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="300"/> + </font> + <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="800"/> + </font> + <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="900"/> </font> <font weight="400" style="normal" index="0" fallbackFor="serif" postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc @@ -1443,8 +1542,41 @@ </font> </family> <family lang="ko"> - <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular"> + <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="100"/> + </font> + <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="200"/> + </font> + <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="300"/> + </font> + <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> + NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="800"/> + </font> + <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc + <axis tag="wght" stylevalue="900"/> </font> <font weight="400" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml deleted file mode 100644 index 8cbc3000c250..000000000000 --- a/data/fonts/fonts_cjkvf.xml +++ /dev/null @@ -1,1795 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - DEPRECATED: This XML file is no longer a source of the font files installed - in the system. - - For the device vendors: please add your font configurations to the - platform/frameworks/base/data/font_fallback.xml and also add it to this XML - file as much as possible for apps that reads this XML file. - - For the application developers: please stop reading this XML file and use - android.graphics.fonts.SystemFonts#getAvailableFonts Java API or - ASystemFontIterator_open NDK API for getting list of system installed - font files. - - WARNING: Parsing of this file by third-party apps is not supported. The - file, and the font files it refers to, will be renamed and/or moved out - from their respective location in the next Android release, and/or the - format or syntax of the file may change significantly. If you parse this - file for information about system fonts, do it at your own risk. Your - application will almost certainly break with the next major Android - release. - - In this file, all fonts without names are added to the default list. - Fonts are chosen based on a match: full BCP-47 language tag including - script, then just language, and finally order (the first font containing - the glyph). - - Order of appearance is also the tiebreaker for weight matching. This is - the reason why the 900 weights of Roboto precede the 700 weights - we - prefer the former when an 800 weight is requested. Since bold spans - effectively add 300 to the weight, this ensures that 900 is the bold - paired with the 500 weight, ensuring adequate contrast. - - TODO(rsheeter) update comment; ordering to match 800 to 900 is no longer required ---> -<familyset version="23"> - <!-- first font is default --> - <family name="sans-serif"> - <font weight="100" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="900" /> - </font> - <font weight="100" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="900" /> - </font> - </family> - - - <!-- Note that aliases must come after the fonts they reference. --> - <alias name="sans-serif-thin" to="sans-serif" weight="100" /> - <alias name="sans-serif-light" to="sans-serif" weight="300" /> - <alias name="sans-serif-medium" to="sans-serif" weight="500" /> - <alias name="sans-serif-black" to="sans-serif" weight="900" /> - <alias name="arial" to="sans-serif" /> - <alias name="helvetica" to="sans-serif" /> - <alias name="tahoma" to="sans-serif" /> - <alias name="verdana" to="sans-serif" /> - - <family name="sans-serif-condensed"> - <font weight="100" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="normal">Roboto-Regular.ttf - <axis tag="ital" stylevalue="0" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="900" /> - </font> - <font weight="100" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="italic">Roboto-Regular.ttf - <axis tag="ital" stylevalue="1" /> - <axis tag="wdth" stylevalue="75" /> - <axis tag="wght" stylevalue="900" /> - </font> - </family> - <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" /> - <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" /> - - <family name="serif"> - <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font> - <font weight="700" style="normal">NotoSerif-Bold.ttf</font> - <font weight="400" style="italic">NotoSerif-Italic.ttf</font> - <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font> - </family> - <alias name="serif-bold" to="serif" weight="700" /> - <alias name="times" to="serif" /> - <alias name="times new roman" to="serif" /> - <alias name="palatino" to="serif" /> - <alias name="georgia" to="serif" /> - <alias name="baskerville" to="serif" /> - <alias name="goudy" to="serif" /> - <alias name="fantasy" to="serif" /> - <alias name="ITC Stone Serif" to="serif" /> - - <family name="monospace"> - <font weight="400" style="normal">DroidSansMono.ttf</font> - </family> - <alias name="sans-serif-monospace" to="monospace" /> - <alias name="monaco" to="monospace" /> - - <family name="serif-monospace"> - <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font> - </family> - <alias name="courier" to="serif-monospace" /> - <alias name="courier new" to="serif-monospace" /> - - <family name="casual"> - <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font> - </family> - - <family name="cursive"> - <font weight="400" style="normal">DancingScript-Regular.ttf - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="700" style="normal">DancingScript-Regular.ttf - <axis tag="wght" stylevalue="700" /> - </font> - </family> - - <family name="sans-serif-smallcaps"> - <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font> - </family> - - <family name="source-sans-pro"> - <font weight="400" style="normal">SourceSansPro-Regular.ttf</font> - <font weight="400" style="italic">SourceSansPro-Italic.ttf</font> - <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font> - <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font> - <font weight="700" style="normal">SourceSansPro-Bold.ttf</font> - <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font> - </family> - <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/> - - <family name="roboto-flex"> - <font weight="100" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="normal">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="0" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="900" /> - </font> - <font weight="100" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="100" /> - </font> - <font weight="200" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="200" /> - </font> - <font weight="300" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="300" /> - </font> - <font weight="400" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="400" /> - </font> - <font weight="500" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="500" /> - </font> - <font weight="600" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="600" /> - </font> - <font weight="700" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="700" /> - </font> - <font weight="800" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="800" /> - </font> - <font weight="900" style="italic">RobotoFlex-Regular.ttf - <axis tag="slnt" stylevalue="-10" /> - <axis tag="wdth" stylevalue="100" /> - <axis tag="wght" stylevalue="900" /> - </font> - </family> - - <!-- fallback fonts --> - <family lang="und-Arab" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoNaskhArabic"> - NotoNaskhArabic-Regular.ttf - </font> - <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font> - </family> - <family lang="und-Arab" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI"> - NotoNaskhArabicUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font> - </family> - <family lang="und-Ethi"> - <font weight="400" style="normal" postScriptName="NotoSansEthiopic-Regular"> - NotoSansEthiopic-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansEthiopic-Regular"> - NotoSansEthiopic-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansEthiopic-Regular"> - NotoSansEthiopic-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansEthiopic-Regular"> - NotoSansEthiopic-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Hebr"> - <font weight="400" style="normal" postScriptName="NotoSansHebrew"> - NotoSansHebrew-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font> - </family> - <family lang="und-Thai" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThai-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif"> - NotoSerifThai-Regular.ttf - </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font> - </family> - <family lang="und-Thai" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansThaiUI"> - NotoSansThaiUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font> - </family> - <family lang="und-Armn"> - <font weight="400" style="normal" postScriptName="NotoSansArmenian-Regular"> - NotoSansArmenian-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansArmenian-Regular"> - NotoSansArmenian-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansArmenian-Regular"> - NotoSansArmenian-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansArmenian-Regular"> - NotoSansArmenian-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Geor,und-Geok"> - <font weight="400" style="normal" postScriptName="NotoSansGeorgian-Regular"> - NotoSansGeorgian-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansGeorgian-Regular"> - NotoSansGeorgian-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansGeorgian-Regular"> - NotoSansGeorgian-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansGeorgian-Regular"> - NotoSansGeorgian-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Deva" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansDevanagari-Regular"> - NotoSansDevanagari-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansDevanagari-Regular"> - NotoSansDevanagari-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansDevanagari-Regular"> - NotoSansDevanagari-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansDevanagari-Regular"> - NotoSansDevanagari-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Deva" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> - NotoSansDevanagariUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> - NotoSansDevanagariUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> - NotoSansDevanagariUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> - NotoSansDevanagariUI-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - - <!-- All scripts of India should come after Devanagari, due to shared - danda characters. - --> - <family lang="und-Gujr" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansGujarati"> - NotoSansGujarati-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Gujr" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI"> - NotoSansGujaratiUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font> - </family> - <family lang="und-Guru" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansGurmukhi-Regular"> - NotoSansGurmukhi-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansGurmukhi-Regular"> - NotoSansGurmukhi-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansGurmukhi-Regular"> - NotoSansGurmukhi-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansGurmukhi-Regular"> - NotoSansGurmukhi-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Guru" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> - NotoSansGurmukhiUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> - NotoSansGurmukhiUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> - NotoSansGurmukhiUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> - NotoSansGurmukhiUI-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Taml" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansTamil-Regular"> - NotoSansTamil-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTamil-Regular"> - NotoSansTamil-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTamil-Regular"> - NotoSansTamil-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTamil-Regular"> - NotoSansTamil-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Taml" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansTamilUI-Regular"> - NotoSansTamilUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTamilUI-Regular"> - NotoSansTamilUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTamilUI-Regular"> - NotoSansTamilUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTamilUI-Regular"> - NotoSansTamilUI-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Mlym" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansMalayalam-Regular"> - NotoSansMalayalam-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansMalayalam-Regular"> - NotoSansMalayalam-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansMalayalam-Regular"> - NotoSansMalayalam-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansMalayalam-Regular"> - NotoSansMalayalam-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Mlym" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> - NotoSansMalayalamUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> - NotoSansMalayalamUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> - NotoSansMalayalamUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> - NotoSansMalayalamUI-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Beng" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansBengali-Regular"> - NotoSansBengali-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansBengali-Regular"> - NotoSansBengali-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansBengali-Regular"> - NotoSansBengali-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansBengali-Regular"> - NotoSansBengali-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Beng" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansBengaliUI-Regular"> - NotoSansBengaliUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansBengaliUI-Regular"> - NotoSansBengaliUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansBengaliUI-Regular"> - NotoSansBengaliUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansBengaliUI-Regular"> - NotoSansBengaliUI-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Telu" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansTelugu-Regular"> - NotoSansTelugu-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTelugu-Regular"> - NotoSansTelugu-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTelugu-Regular"> - NotoSansTelugu-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTelugu-Regular"> - NotoSansTelugu-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Telu" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansTeluguUI-Regular"> - NotoSansTeluguUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTeluguUI-Regular"> - NotoSansTeluguUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTeluguUI-Regular"> - NotoSansTeluguUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTeluguUI-Regular"> - NotoSansTeluguUI-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Knda" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansKannada-Regular"> - NotoSansKannada-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansKannada-Regular"> - NotoSansKannada-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansKannada-Regular"> - NotoSansKannada-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansKannada-Regular"> - NotoSansKannada-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Knda" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansKannadaUI-Regular"> - NotoSansKannadaUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansKannadaUI-Regular"> - NotoSansKannadaUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansKannadaUI-Regular"> - NotoSansKannadaUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansKannadaUI-Regular"> - NotoSansKannadaUI-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Orya" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font> - </family> - <family lang="und-Orya" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansOriyaUI"> - NotoSansOriyaUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font> - </family> - <family lang="und-Sinh" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansSinhala-Regular"> - NotoSansSinhala-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansSinhala-Regular"> - NotoSansSinhala-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansSinhala-Regular"> - NotoSansSinhala-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansSinhala-Regular"> - NotoSansSinhala-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="400" style="normal" fallbackFor="serif" - postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" fallbackFor="serif" - postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" fallbackFor="serif" - postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" fallbackFor="serif" - postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Sinh" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> - NotoSansSinhalaUI-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> - NotoSansSinhalaUI-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> - NotoSansSinhalaUI-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> - NotoSansSinhalaUI-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Khmr" variant="elegant"> - <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="26.0"/> - </font> - <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="39.0"/> - </font> - <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="58.0"/> - </font> - <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="90.0"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="108.0"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="128.0"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="151.0"/> - </font> - <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="169.0"/> - </font> - <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular"> - NotoSansKhmer-VF.ttf - <axis tag="wdth" stylevalue="100.0"/> - <axis tag="wght" stylevalue="190.0"/> - </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font> - </family> - <family lang="und-Khmr" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansKhmerUI"> - NotoSansKhmerUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font> - </family> - <family lang="und-Laoo" variant="elegant"> - <font weight="400" style="normal">NotoSansLao-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansLao-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif"> - NotoSerifLao-Regular.ttf - </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> - </family> - <family lang="und-Laoo" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> - </family> - <family lang="und-Mymr" variant="elegant"> - <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font> - <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font> - <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font> - </family> - <family lang="und-Mymr" variant="compact"> - <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font> - <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font> - <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font> - </family> - <family lang="und-Thaa"> - <font weight="400" style="normal" postScriptName="NotoSansThaana"> - NotoSansThaana-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font> - </family> - <family lang="und-Cham"> - <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf - </font> - <font weight="700" style="normal">NotoSansCham-Bold.ttf</font> - </family> - <family lang="und-Ahom"> - <font weight="400" style="normal">NotoSansAhom-Regular.otf</font> - </family> - <family lang="und-Adlm"> - <font weight="400" style="normal" postScriptName="NotoSansAdlam-Regular"> - NotoSansAdlam-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansAdlam-Regular"> - NotoSansAdlam-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansAdlam-Regular"> - NotoSansAdlam-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansAdlam-Regular"> - NotoSansAdlam-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Avst"> - <font weight="400" style="normal" postScriptName="NotoSansAvestan"> - NotoSansAvestan-Regular.ttf - </font> - </family> - <family lang="und-Bali"> - <font weight="400" style="normal" postScriptName="NotoSansBalinese"> - NotoSansBalinese-Regular.ttf - </font> - </family> - <family lang="und-Bamu"> - <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf - </font> - </family> - <family lang="und-Batk"> - <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf - </font> - </family> - <family lang="und-Brah"> - <font weight="400" style="normal" postScriptName="NotoSansBrahmi"> - NotoSansBrahmi-Regular.ttf - </font> - </family> - <family lang="und-Bugi"> - <font weight="400" style="normal" postScriptName="NotoSansBuginese"> - NotoSansBuginese-Regular.ttf - </font> - </family> - <family lang="und-Buhd"> - <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf - </font> - </family> - <family lang="und-Cans"> - <font weight="400" style="normal"> - NotoSansCanadianAboriginal-Regular.ttf - </font> - </family> - <family lang="und-Cari"> - <font weight="400" style="normal" postScriptName="NotoSansCarian"> - NotoSansCarian-Regular.ttf - </font> - </family> - <family lang="und-Cakm"> - <font weight="400" style="normal">NotoSansChakma-Regular.otf</font> - </family> - <family lang="und-Cher"> - <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font> - </family> - <family lang="und-Copt"> - <font weight="400" style="normal" postScriptName="NotoSansCoptic"> - NotoSansCoptic-Regular.ttf - </font> - </family> - <family lang="und-Xsux"> - <font weight="400" style="normal" postScriptName="NotoSansCuneiform"> - NotoSansCuneiform-Regular.ttf - </font> - </family> - <family lang="und-Cprt"> - <font weight="400" style="normal" postScriptName="NotoSansCypriot"> - NotoSansCypriot-Regular.ttf - </font> - </family> - <family lang="und-Dsrt"> - <font weight="400" style="normal" postScriptName="NotoSansDeseret"> - NotoSansDeseret-Regular.ttf - </font> - </family> - <family lang="und-Egyp"> - <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs"> - NotoSansEgyptianHieroglyphs-Regular.ttf - </font> - </family> - <family lang="und-Elba"> - <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font> - </family> - <family lang="und-Glag"> - <font weight="400" style="normal" postScriptName="NotoSansGlagolitic"> - NotoSansGlagolitic-Regular.ttf - </font> - </family> - <family lang="und-Goth"> - <font weight="400" style="normal" postScriptName="NotoSansGothic"> - NotoSansGothic-Regular.ttf - </font> - </family> - <family lang="und-Hano"> - <font weight="400" style="normal" postScriptName="NotoSansHanunoo"> - NotoSansHanunoo-Regular.ttf - </font> - </family> - <family lang="und-Armi"> - <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic"> - NotoSansImperialAramaic-Regular.ttf - </font> - </family> - <family lang="und-Phli"> - <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi"> - NotoSansInscriptionalPahlavi-Regular.ttf - </font> - </family> - <family lang="und-Prti"> - <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian"> - NotoSansInscriptionalParthian-Regular.ttf - </font> - </family> - <family lang="und-Java"> - <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font> - </family> - <family lang="und-Kthi"> - <font weight="400" style="normal" postScriptName="NotoSansKaithi"> - NotoSansKaithi-Regular.ttf - </font> - </family> - <family lang="und-Kali"> - <font weight="400" style="normal" postScriptName="NotoSansKayahLi"> - NotoSansKayahLi-Regular.ttf - </font> - </family> - <family lang="und-Khar"> - <font weight="400" style="normal" postScriptName="NotoSansKharoshthi"> - NotoSansKharoshthi-Regular.ttf - </font> - </family> - <family lang="und-Lepc"> - <font weight="400" style="normal" postScriptName="NotoSansLepcha"> - NotoSansLepcha-Regular.ttf - </font> - </family> - <family lang="und-Limb"> - <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf - </font> - </family> - <family lang="und-Linb"> - <font weight="400" style="normal" postScriptName="NotoSansLinearB"> - NotoSansLinearB-Regular.ttf - </font> - </family> - <family lang="und-Lisu"> - <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf - </font> - </family> - <family lang="und-Lyci"> - <font weight="400" style="normal" postScriptName="NotoSansLycian"> - NotoSansLycian-Regular.ttf - </font> - </family> - <family lang="und-Lydi"> - <font weight="400" style="normal" postScriptName="NotoSansLydian"> - NotoSansLydian-Regular.ttf - </font> - </family> - <family lang="und-Mand"> - <font weight="400" style="normal" postScriptName="NotoSansMandaic"> - NotoSansMandaic-Regular.ttf - </font> - </family> - <family lang="und-Mtei"> - <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek"> - NotoSansMeeteiMayek-Regular.ttf - </font> - </family> - <family lang="und-Talu"> - <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue"> - NotoSansNewTaiLue-Regular.ttf - </font> - </family> - <family lang="und-Nkoo"> - <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf - </font> - </family> - <family lang="und-Ogam"> - <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf - </font> - </family> - <family lang="und-Olck"> - <font weight="400" style="normal" postScriptName="NotoSansOlChiki"> - NotoSansOlChiki-Regular.ttf - </font> - </family> - <family lang="und-Ital"> - <font weight="400" style="normal" postScriptName="NotoSansOldItalic"> - NotoSansOldItalic-Regular.ttf - </font> - </family> - <family lang="und-Xpeo"> - <font weight="400" style="normal" postScriptName="NotoSansOldPersian"> - NotoSansOldPersian-Regular.ttf - </font> - </family> - <family lang="und-Sarb"> - <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian"> - NotoSansOldSouthArabian-Regular.ttf - </font> - </family> - <family lang="und-Orkh"> - <font weight="400" style="normal" postScriptName="NotoSansOldTurkic"> - NotoSansOldTurkic-Regular.ttf - </font> - </family> - <family lang="und-Osge"> - <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font> - </family> - <family lang="und-Osma"> - <font weight="400" style="normal" postScriptName="NotoSansOsmanya"> - NotoSansOsmanya-Regular.ttf - </font> - </family> - <family lang="und-Phnx"> - <font weight="400" style="normal" postScriptName="NotoSansPhoenician"> - NotoSansPhoenician-Regular.ttf - </font> - </family> - <family lang="und-Rjng"> - <font weight="400" style="normal" postScriptName="NotoSansRejang"> - NotoSansRejang-Regular.ttf - </font> - </family> - <family lang="und-Runr"> - <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf - </font> - </family> - <family lang="und-Samr"> - <font weight="400" style="normal" postScriptName="NotoSansSamaritan"> - NotoSansSamaritan-Regular.ttf - </font> - </family> - <family lang="und-Saur"> - <font weight="400" style="normal" postScriptName="NotoSansSaurashtra"> - NotoSansSaurashtra-Regular.ttf - </font> - </family> - <family lang="und-Shaw"> - <font weight="400" style="normal" postScriptName="NotoSansShavian"> - NotoSansShavian-Regular.ttf - </font> - </family> - <family lang="und-Sund"> - <font weight="400" style="normal" postScriptName="NotoSansSundanese"> - NotoSansSundanese-Regular.ttf - </font> - </family> - <family lang="und-Sylo"> - <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri"> - NotoSansSylotiNagri-Regular.ttf - </font> - </family> - <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. --> - <family lang="und-Syre"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela"> - NotoSansSyriacEstrangela-Regular.ttf - </font> - </family> - <family lang="und-Syrn"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern"> - NotoSansSyriacEastern-Regular.ttf - </font> - </family> - <family lang="und-Syrj"> - <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern"> - NotoSansSyriacWestern-Regular.ttf - </font> - </family> - <family lang="und-Tglg"> - <font weight="400" style="normal" postScriptName="NotoSansTagalog"> - NotoSansTagalog-Regular.ttf - </font> - </family> - <family lang="und-Tagb"> - <font weight="400" style="normal" postScriptName="NotoSansTagbanwa"> - NotoSansTagbanwa-Regular.ttf - </font> - </family> - <family lang="und-Lana"> - <font weight="400" style="normal" postScriptName="NotoSansTaiTham"> - NotoSansTaiTham-Regular.ttf - </font> - </family> - <family lang="und-Tavt"> - <font weight="400" style="normal" postScriptName="NotoSansTaiViet"> - NotoSansTaiViet-Regular.ttf - </font> - </family> - <family lang="und-Tibt"> - <font weight="400" style="normal" postScriptName="NotoSerifTibetan-Regular"> - NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSerifTibetan-Regular"> - NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSerifTibetan-Regular"> - NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSerifTibetan-Regular"> - NotoSerifTibetan-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Tfng"> - <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font> - </family> - <family lang="und-Ugar"> - <font weight="400" style="normal" postScriptName="NotoSansUgaritic"> - NotoSansUgaritic-Regular.ttf - </font> - </family> - <family lang="und-Vaii"> - <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf - </font> - </family> - <family> - <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> - </family> - <family lang="zh-Hans"> - <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="100"/> - </font> - <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="200"/> - </font> - <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="300"/> - </font> - <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="800"/> - </font> - <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="900"/> - </font> - <font weight="400" style="normal" index="2" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="zh-Hant,zh-Bopo"> - <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="100"/> - </font> - <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="200"/> - </font> - <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="300"/> - </font> - <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="800"/> - </font> - <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="900"/> - </font> - <font weight="400" style="normal" index="3" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="ja"> - <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="100"/> - </font> - <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="200"/> - </font> - <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="300"/> - </font> - <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="800"/> - </font> - <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="900"/> - </font> - <font weight="400" style="normal" index="0" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="ja"> - <font weight="400" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight"> - NotoSerifHentaigana.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight"> - NotoSerifHentaigana.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="ko"> - <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="100"/> - </font> - <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="200"/> - </font> - <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="300"/> - </font> - <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="700"/> - </font> - <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="800"/> - </font> - <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> - NotoSansCJK-Regular.ttc - <axis tag="wght" stylevalue="900"/> - </font> - <font weight="400" style="normal" index="1" fallbackFor="serif" - postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc - </font> - </family> - <family lang="und-Zsye" ignore="true"> - <font weight="400" style="normal">NotoColorEmojiLegacy.ttf</font> - </family> - <family lang="und-Zsye"> - <font weight="400" style="normal">NotoColorEmoji.ttf</font> - </family> - <family lang="und-Zsye"> - <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font> - </family> - <family lang="und-Zsym"> - <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font> - </family> - <!-- - Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't - override the East Asian punctuation for Chinese. - --> - <family lang="und-Tale"> - <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf - </font> - </family> - <family lang="und-Yiii"> - <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font> - </family> - <family lang="und-Mong"> - <font weight="400" style="normal" postScriptName="NotoSansMongolian"> - NotoSansMongolian-Regular.ttf - </font> - </family> - <family lang="und-Phag"> - <font weight="400" style="normal" postScriptName="NotoSansPhagsPa"> - NotoSansPhagsPa-Regular.ttf - </font> - </family> - <family lang="und-Hluw"> - <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font> - </family> - <family lang="und-Bass"> - <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font> - </family> - <family lang="und-Bhks"> - <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font> - </family> - <family lang="und-Hatr"> - <font weight="400" style="normal">NotoSansHatran-Regular.otf</font> - </family> - <family lang="und-Lina"> - <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font> - </family> - <family lang="und-Mani"> - <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font> - </family> - <family lang="und-Marc"> - <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font> - </family> - <family lang="und-Merc"> - <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font> - </family> - <family lang="und-Plrd"> - <font weight="400" style="normal">NotoSansMiao-Regular.otf</font> - </family> - <family lang="und-Mroo"> - <font weight="400" style="normal">NotoSansMro-Regular.otf</font> - </family> - <family lang="und-Mult"> - <font weight="400" style="normal">NotoSansMultani-Regular.otf</font> - </family> - <family lang="und-Nbat"> - <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font> - </family> - <family lang="und-Newa"> - <font weight="400" style="normal">NotoSansNewa-Regular.otf</font> - </family> - <family lang="und-Narb"> - <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font> - </family> - <family lang="und-Perm"> - <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font> - </family> - <family lang="und-Hmng"> - <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font> - </family> - <family lang="und-Palm"> - <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font> - </family> - <family lang="und-Pauc"> - <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font> - </family> - <family lang="und-Shrd"> - <font weight="400" style="normal">NotoSansSharada-Regular.otf</font> - </family> - <family lang="und-Sora"> - <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font> - </family> - <family lang="und-Gong"> - <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font> - </family> - <family lang="und-Rohg"> - <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font> - </family> - <family lang="und-Khoj"> - <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font> - </family> - <family lang="und-Gonm"> - <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font> - </family> - <family lang="und-Wcho"> - <font weight="400" style="normal">NotoSansWancho-Regular.otf</font> - </family> - <family lang="und-Wara"> - <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font> - </family> - <family lang="und-Gran"> - <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font> - </family> - <family lang="und-Modi"> - <font weight="400" style="normal">NotoSansModi-Regular.ttf</font> - </family> - <family lang="und-Dogr"> - <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font> - </family> - <family lang="und-Medf"> - <font weight="400" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> - NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> - NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> - NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> - NotoSansMedefaidrin-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Soyo"> - <font weight="400" style="normal" postScriptName="NotoSansSoyombo-Regular"> - NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansSoyombo-Regular"> - NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansSoyombo-Regular"> - NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansSoyombo-Regular"> - NotoSansSoyombo-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Takr"> - <font weight="400" style="normal" postScriptName="NotoSansTakri-Regular"> - NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSansTakri-Regular"> - NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSansTakri-Regular"> - NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSansTakri-Regular"> - NotoSansTakri-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Hmnp"> - <font weight="400" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> - NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> - NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> - NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> - NotoSerifNyiakengPuachueHmong-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> - <family lang="und-Yezi"> - <font weight="400" style="normal" postScriptName="NotoSerifYezidi-Regular"> - NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="400"/> - </font> - <font weight="500" style="normal" postScriptName="NotoSerifYezidi-Regular"> - NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="500"/> - </font> - <font weight="600" style="normal" postScriptName="NotoSerifYezidi-Regular"> - NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="600"/> - </font> - <font weight="700" style="normal" postScriptName="NotoSerifYezidi-Regular"> - NotoSerifYezidi-VF.ttf - <axis tag="wght" stylevalue="700"/> - </font> - </family> -</familyset> diff --git a/keystore/java/android/security/OWNERS b/keystore/java/android/security/OWNERS index 32759b2402a9..43052866b938 100644 --- a/keystore/java/android/security/OWNERS +++ b/keystore/java/android/security/OWNERS @@ -1,2 +1,2 @@ -per-file *.java,*.aidl = eranm@google.com,pgrafov@google.com,rubinxu@google.com +per-file *.java,*.aidl = drysdale@google.com,jbires@google.com,pgrafov@google.com,rubinxu@google.com per-file KeyStoreManager.java = mpgroover@google.com diff --git a/keystore/tests/OWNERS b/keystore/tests/OWNERS index 86c31f403da0..0f94ddcee47f 100644 --- a/keystore/tests/OWNERS +++ b/keystore/tests/OWNERS @@ -1,4 +1,7 @@ +# Android HW Trust team +drysdale@google.com +jbires@google.com + # Android Enterprise security team -eranm@google.com pgrafov@google.com rubinxu@google.com diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java index a3d2d7f4dcdf..438532725686 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -16,6 +16,10 @@ package androidx.window.extensions.area; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER; import android.app.Activity; @@ -23,6 +27,7 @@ import android.content.Context; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateRequest; +import android.hardware.devicestate.feature.flags.Flags; import android.hardware.display.DisplayManager; import android.util.ArraySet; import android.util.DisplayMetrics; @@ -72,18 +77,18 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @GuardedBy("mLock") private final ArraySet<Consumer<ExtensionWindowAreaStatus>> mRearDisplayPresentationStatusListeners = new ArraySet<>(); - private final int mRearDisplayState; + private int mRearDisplayState = INVALID_DEVICE_STATE_IDENTIFIER; private final int mConcurrentDisplayState; @NonNull - private final int[] mFoldedDeviceStates; + private int[] mFoldedDeviceStates = new int[0]; private long mRearDisplayAddress = INVALID_DISPLAY_ADDRESS; @WindowAreaSessionState private int mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE; @GuardedBy("mLock") - private int mCurrentDeviceState = INVALID_DEVICE_STATE_IDENTIFIER; + private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE; @GuardedBy("mLock") - private int[] mCurrentSupportedDeviceStates; + private List<DeviceState> mCurrentSupportedDeviceStates; @GuardedBy("mLock") private DeviceStateRequest mRearDisplayStateRequest; @@ -103,16 +108,25 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mDisplayManager = context.getSystemService(DisplayManager.class); mExecutor = context.getMainExecutor(); - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentSupportedDeviceStates = getSupportedStateIdentifiers( - mDeviceStateManager.getSupportedDeviceStates()); - mFoldedDeviceStates = context.getResources().getIntArray( - R.array.config_foldedDeviceStates); + mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedDeviceStates(); - // TODO(b/236022708) Move rear display state to device state config file - mRearDisplayState = context.getResources().getInteger( - R.integer.config_deviceStateRearDisplay); + if (Flags.deviceStatePropertyMigration()) { + for (int i = 0; i < mCurrentSupportedDeviceStates.size(); i++) { + DeviceState state = mCurrentSupportedDeviceStates.get(i); + if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)) { + mRearDisplayState = state.getIdentifier(); + break; + } + } + } else { + mFoldedDeviceStates = context.getResources().getIntArray( + R.array.config_foldedDeviceStates); + // TODO(b/236022708) Move rear display state to device state config file + mRearDisplayState = context.getResources().getInteger( + R.integer.config_deviceStateRearDisplay); + } + // TODO(b/374351956) Use DeviceState API when the dual display state is always returned mConcurrentDisplayState = context.getResources().getInteger( R.integer.config_deviceStateConcurrentRearDisplay); @@ -147,7 +161,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mRearDisplayStatusListeners.add(consumer); // If current device state is still invalid, the initial value has not been provided. - if (mCurrentDeviceState == INVALID_DEVICE_STATE_IDENTIFIER) { + if (mCurrentDeviceState.getIdentifier() == INVALID_DEVICE_STATE_IDENTIFIER) { return; } consumer.accept(getCurrentRearDisplayModeStatus()); @@ -312,7 +326,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mRearDisplayPresentationStatusListeners.add(consumer); // If current device state is still invalid, the initial value has not been provided - if (mCurrentDeviceState == INVALID_DEVICE_STATE_IDENTIFIER) { + if (mCurrentDeviceState.getIdentifier() == INVALID_DEVICE_STATE_IDENTIFIER) { return; } @WindowAreaStatus int currentStatus = getCurrentRearDisplayPresentationModeStatus(); @@ -452,8 +466,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @Override public void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) { synchronized (mLock) { - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentSupportedDeviceStates = getSupportedStateIdentifiers(supportedStates); + mCurrentSupportedDeviceStates = supportedStates; updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus()); updateRearDisplayPresentationStatusListeners( getCurrentRearDisplayPresentationModeStatus()); @@ -463,8 +476,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @Override public void onDeviceStateChanged(@NonNull DeviceState state) { synchronized (mLock) { - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentDeviceState = state.getIdentifier(); + mCurrentDeviceState = state; updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus()); updateRearDisplayPresentationStatusListeners( getCurrentRearDisplayPresentationModeStatus()); @@ -477,7 +489,8 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_UNSUPPORTED; } - if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mRearDisplayState)) { + if (!deviceStateListContainsIdentifier(mCurrentSupportedDeviceStates, + mRearDisplayState)) { return WindowAreaComponent.STATUS_UNAVAILABLE; } @@ -488,15 +501,6 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_AVAILABLE; } - // TODO(b/329436166): Remove and update the usage of device state manager API's - private int[] getSupportedStateIdentifiers(@NonNull List<DeviceState> states) { - int[] identifiers = new int[states.size()]; - for (int i = 0; i < states.size(); i++) { - identifiers[i] = states.get(i).getIdentifier(); - } - return identifiers; - } - /** * Helper method to determine if a rear display session is currently active by checking * if the current device state is that which corresponds to {@code mRearDisplayState}. @@ -505,7 +509,31 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, */ @GuardedBy("mLock") private boolean isRearDisplayActive() { - return mCurrentDeviceState == mRearDisplayState; + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY); + } else { + return mCurrentDeviceState.getIdentifier() == mRearDisplayState; + } + } + + @GuardedBy("mLock") + private boolean isRearDisplayPresentationModeActive() { + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty(PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT); + } else { + return mCurrentDeviceState.getIdentifier() == mConcurrentDisplayState; + } + } + + @GuardedBy("mLock") + private boolean deviceStateListContainsIdentifier(List<DeviceState> deviceStates, + int identifier) { + for (int i = 0; i < deviceStates.size(); i++) { + if (deviceStates.get(i).getIdentifier() == identifier) { + return true; + } + } + return false; } @GuardedBy("mLock") @@ -526,12 +554,12 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_UNSUPPORTED; } - if (mCurrentDeviceState == mConcurrentDisplayState) { + if (isRearDisplayPresentationModeActive()) { return WindowAreaComponent.STATUS_ACTIVE; } - if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState) - || isDeviceFolded()) { + if (!deviceStateListContainsIdentifier(mCurrentSupportedDeviceStates, + mConcurrentDisplayState) || isDeviceFolded()) { return WindowAreaComponent.STATUS_UNAVAILABLE; } return WindowAreaComponent.STATUS_AVAILABLE; @@ -539,7 +567,12 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @GuardedBy("mLock") private boolean isDeviceFolded() { - return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState); + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY); + } else { + return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState.getIdentifier()); + } } @GuardedBy("mLock") diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java index 6ad2f088ce95..220fc6f82a71 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java @@ -54,6 +54,7 @@ class BackupHelper { @NonNull private final BackupIdler mBackupIdler = new BackupIdler(); private boolean mBackupIdlerScheduled; + private boolean mSaveEmbeddingState = false; private final List<ParcelableTaskContainerData> mParcelableTaskContainerDataList = new ArrayList<>(); @@ -71,11 +72,32 @@ class BackupHelper { } } + void setAutoSaveEmbeddingState(boolean saveEmbeddingState) { + if (mSaveEmbeddingState == saveEmbeddingState) { + return; + } + + Log.i(TAG, "Set save embedding state: " + saveEmbeddingState); + mSaveEmbeddingState = saveEmbeddingState; + if (!mSaveEmbeddingState) { + removeSavedState(); + return; + } + + if (!hasPendingStateToRestore() && !mController.getTaskContainers().isEmpty()) { + scheduleBackup(); + } + } /** * Schedules a back-up request. It is no-op if there was a request scheduled and not yet * completed. */ void scheduleBackup() { + if (!mSaveEmbeddingState) { + // TODO(b/289875940): enabled internally for broader testing. + return; + } + if (!mBackupIdlerScheduled) { mBackupIdlerScheduled = true; Looper.getMainLooper().getQueue().addIdleHandler(mBackupIdler); @@ -128,7 +150,6 @@ class BackupHelper { final List<TaskFragmentInfo> infos = savedState.getParcelableArrayList( KEY_RESTORE_TASK_FRAGMENTS_INFO, TaskFragmentInfo.class); for (TaskFragmentInfo info : infos) { - if (DEBUG) Log.d(TAG, "Retrieved: " + info); mTaskFragmentInfos.put(info.getFragmentToken(), info); mPresenter.updateTaskFragmentInfo(info); } @@ -140,6 +161,11 @@ class BackupHelper { if (DEBUG) Log.d(TAG, "Retrieved: " + info); mTaskFragmentParentInfos.put(info.getTaskId(), info); } + + if (DEBUG) { + Log.d(TAG, "Retrieved task-fragment info: " + infos.size() + ", task info: " + + parentInfos.size()); + } } void abortTaskContainerRebuilding(@NonNull WindowContainerTransaction wct) { @@ -148,7 +174,6 @@ class BackupHelper { final TaskFragmentInfo info = mTaskFragmentInfos.valueAt(i); mPresenter.deleteTaskFragment(wct, info.getFragmentToken()); } - removeSavedState(); } @@ -190,6 +215,9 @@ class BackupHelper { final ArrayMap<String, EmbeddingRule> embeddingRuleMap = new ArrayMap<>(); for (EmbeddingRule rule : rules) { embeddingRuleMap.put(rule.getTag(), rule); + if (DEBUG) { + Log.d(TAG, "Tag: " + rule.getTag() + " rule: " + rule); + } } boolean restoredAny = false; @@ -201,7 +229,7 @@ class BackupHelper { // has unknown tag, unable to restore. if (DEBUG) { Log.d(TAG, "Rebuilding TaskContainer abort! Unknown Tag. Task#" - + parcelableTaskContainerData.mTaskId); + + parcelableTaskContainerData.mTaskId + ", tags = " + tags); } continue; } @@ -217,7 +245,7 @@ class BackupHelper { final TaskContainer taskContainer = new TaskContainer(parcelableTaskContainerData, mController, mTaskFragmentInfos); - if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer); + if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer.getTaskId()); mController.addTaskContainer(taskContainer.getTaskId(), taskContainer); for (ParcelableSplitContainerData splitData : diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 3368e2eab3ad..60e1a506ab73 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -2886,6 +2886,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return getActiveSplitForContainer(container) != null; } + + @Override + public void setAutoSaveEmbeddingState(boolean saveEmbeddingState) { + if (!Flags.aeBackStackRestore()) { + return; + } + + synchronized (mLock) { + mPresenter.setAutoSaveEmbeddingState(saveEmbeddingState); + } + } + void scheduleBackup() { synchronized (mLock) { mPresenter.scheduleBackup(); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index b498ee2ff438..9a2f32e9ee99 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -183,6 +183,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } } + void setAutoSaveEmbeddingState(boolean saveEmbeddingState) { + mBackupHelper.setAutoSaveEmbeddingState(saveEmbeddingState); + } + void scheduleBackup() { mBackupHelper.scheduleBackup(); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index b453f1d4e936..6928409fd819 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -48,8 +48,6 @@ import androidx.window.extensions.embedding.SplitAttributes.SplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType; -import com.android.window.flags.Flags; - import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -634,11 +632,7 @@ class TaskContainer { // pin container. updateAlwaysOnTopOverlayIfNecessary(); - // TODO(b/289875940): Making backup-restore as an opt-in solution, before the flag goes - // to next-food. - if (Flags.aeBackStackRestore()) { - mSplitController.scheduleBackup(); - } + mSplitController.scheduleBackup(); } private void updateAlwaysOnTopOverlayIfNecessary() { diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml index 1260796810c2..b2ac640a468d 100644 --- a/libs/WindowManager/Shell/AndroidManifest.xml +++ b/libs/WindowManager/Shell/AndroidManifest.xml @@ -25,6 +25,7 @@ <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" /> <uses-permission android:name="android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION" /> + <uses-permission android:name="android.permission.MANAGE_KEY_GESTURES" /> <application> <activity diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt new file mode 100644 index 000000000000..2d6df43f67e0 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt @@ -0,0 +1,40 @@ +/* + * 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.bubbles + +import com.android.internal.logging.testing.UiEventLoggerFake.FakeUiEvent +import com.google.common.truth.FailureMetadata +import com.google.common.truth.Subject +import com.google.common.truth.Truth + +/** Subclass of [Subject] to simplify verifying [FakeUiEvent] data */ +class UiEventSubject(metadata: FailureMetadata, private val actual: FakeUiEvent) : + Subject(metadata, actual) { + + /** Check that [FakeUiEvent] contains the expected data from the [bubble] passed id */ + fun hasBubbleInfo(bubble: Bubble) { + check("uid").that(actual.uid).isEqualTo(bubble.appUid) + check("packageName").that(actual.packageName).isEqualTo(bubble.packageName) + check("instanceId").that(actual.instanceId).isEqualTo(bubble.instanceId) + } + + companion object { + @JvmStatic + fun assertThat(event: FakeUiEvent): UiEventSubject = + Truth.assertAbout(Factory(::UiEventSubject)).that(event) + } +} diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubjectTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubjectTest.kt new file mode 100644 index 000000000000..af238d033aee --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubjectTest.kt @@ -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.wm.shell.bubbles + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.logging.InstanceId +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.internal.logging.testing.UiEventLoggerFake.FakeUiEvent +import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat +import com.google.common.truth.ExpectFailure.assertThat +import com.google.common.truth.ExpectFailure.expectFailure +import com.google.common.truth.Subject +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.kotlin.whenever + +/** Test for [UiEventSubject] */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class UiEventSubjectTest { + + private val uiEventSubjectFactory = + Subject.Factory<UiEventSubject, FakeUiEvent> { metadata, actual -> + UiEventSubject(metadata, actual) + } + + private lateinit var uiEventLoggerFake: UiEventLoggerFake + + @Before + fun setUp() { + uiEventLoggerFake = UiEventLoggerFake() + } + + @Test + fun test_bubbleLogEvent_hasBubbleInfo() { + val bubble = + createBubble( + appUid = 1, + packageName = "test", + instanceId = InstanceId.fakeInstanceId(2), + ) + BubbleLogger(uiEventLoggerFake).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED) + val uiEvent = uiEventLoggerFake.logs.first() + + // Check that fields match the expected values + assertThat(uiEvent.uid).isEqualTo(1) + assertThat(uiEvent.packageName).isEqualTo("test") + assertThat(uiEvent.instanceId.id).isEqualTo(2) + + // Check that hasBubbleInfo condition passes + assertThat(uiEvent).hasBubbleInfo(bubble) + } + + @Test + fun test_bubbleLogEvent_uidMismatch() { + val bubble = + createBubble( + appUid = 1, + packageName = "test", + instanceId = InstanceId.fakeInstanceId(2), + ) + BubbleLogger(uiEventLoggerFake).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED) + val uiEvent = uiEventLoggerFake.logs.first() + + // Change uid to have a mismatch + val otherBubble = bubble.copy(appUid = 99) + + val failure = expectFailure { test -> + test.about(uiEventSubjectFactory).that(uiEvent).hasBubbleInfo(otherBubble) + } + assertThat(failure).factValue("value of").isEqualTo("uiEvent.uid") + } + + @Test + fun test_bubbleLogEvent_packageNameMismatch() { + val bubble = + createBubble( + appUid = 1, + packageName = "test", + instanceId = InstanceId.fakeInstanceId(2), + ) + BubbleLogger(uiEventLoggerFake).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED) + val uiEvent = uiEventLoggerFake.logs.first() + + // Change package name to have a mismatch + val otherBubble = bubble.copy(packageName = "somethingelse") + + val failure = expectFailure { test -> + test.about(uiEventSubjectFactory).that(uiEvent).hasBubbleInfo(otherBubble) + } + assertThat(failure).factValue("value of").isEqualTo("uiEvent.packageName") + } + + @Test + fun test_bubbleLogEvent_instanceIdMismatch() { + val bubble = + createBubble( + appUid = 1, + packageName = "test", + instanceId = InstanceId.fakeInstanceId(2), + ) + BubbleLogger(uiEventLoggerFake).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED) + val uiEvent = uiEventLoggerFake.logs.first() + + // Change instance id to have a mismatch + val otherBubble = bubble.copy(instanceId = InstanceId.fakeInstanceId(99)) + + val failure = expectFailure { test -> + test.about(uiEventSubjectFactory).that(uiEvent).hasBubbleInfo(otherBubble) + } + assertThat(failure).factValue("value of").isEqualTo("uiEvent.instanceId") + } + + private fun createBubble(appUid: Int, packageName: String, instanceId: InstanceId): Bubble { + return mock(Bubble::class.java).apply { + whenever(getAppUid()).thenReturn(appUid) + whenever(getPackageName()).thenReturn(packageName) + whenever(getInstanceId()).thenReturn(instanceId) + } + } + + private fun Bubble.copy( + appUid: Int = this.appUid, + packageName: String = this.packageName, + instanceId: InstanceId = this.instanceId, + ): Bubble { + return createBubble(appUid, packageName, instanceId) + } +} diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt index fa9d2baa78d9..08d647de4a51 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt @@ -40,6 +40,7 @@ import com.android.wm.shell.bubbles.BubbleTaskView import com.android.wm.shell.bubbles.BubbleTaskViewFactory import com.android.wm.shell.bubbles.DeviceConfig import com.android.wm.shell.bubbles.RegionSamplingProvider +import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.shared.bubbles.BubbleBarLocation import com.android.wm.shell.shared.handles.RegionSamplingHelper @@ -47,16 +48,14 @@ import com.android.wm.shell.taskview.TaskView import com.android.wm.shell.taskview.TaskViewTaskController import com.google.common.truth.Truth.assertThat import com.google.common.util.concurrent.MoreExecutors.directExecutor +import java.util.Collections +import java.util.concurrent.Executor import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock -import org.mockito.kotlin.spy -import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import java.util.Collections -import java.util.concurrent.Executor /** Tests for [BubbleBarExpandedViewTest] */ @SmallTest @@ -82,7 +81,7 @@ class BubbleBarExpandedViewTest { private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null private var regionSamplingProvider: TestRegionSamplingProvider? = null - private val bubbleLogger = spy(BubbleLogger(UiEventLoggerFake())) + private val uiEventLoggerFake = UiEventLoggerFake() @Before fun setUp() { @@ -116,7 +115,7 @@ class BubbleBarExpandedViewTest { bubbleExpandedView.initialize( expandedViewManager, positioner, - bubbleLogger, + BubbleLogger(uiEventLoggerFake), false /* isOverflow */, bubbleTaskView, mainExecutor, @@ -223,7 +222,10 @@ class BubbleBarExpandedViewTest { bubbleExpandedView.findViewWithTag<View>(BubbleBarMenuView.DISMISS_ACTION_TAG) assertThat(dismissMenuItem).isNotNull() getInstrumentation().runOnMainSync { dismissMenuItem.performClick() } - verify(bubbleLogger).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + assertThat(uiEventLoggerFake.logs[0].eventId) + .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU.id) + assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble) } private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java index 9ca9b730fb06..4569cf31dab1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java @@ -26,6 +26,7 @@ import android.util.Log; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; import android.window.IBackAnimationRunner; import android.window.IOnBackInvokedCallback; @@ -34,6 +35,8 @@ import com.android.internal.jank.Cuj.CujType; import com.android.internal.jank.InteractionJankMonitor; import com.android.wm.shell.shared.annotations.ShellMainThread; +import java.lang.ref.WeakReference; + /** * Used to register the animation callback and runner, it will trigger result if gesture was finish * before it received IBackAnimationRunner#onAnimationStart, so the controller could continue @@ -101,6 +104,40 @@ public class BackAnimationRunner { return mCallback; } + private Runnable mFinishedCallback; + private RemoteAnimationTarget[] mApps; + private IRemoteAnimationFinishedCallback mRemoteCallback; + + private static class RemoteAnimationFinishedStub extends IRemoteAnimationFinishedCallback.Stub { + //the binder callback should not hold strong reference to it to avoid memory leak. + private WeakReference<BackAnimationRunner> mRunnerRef; + + private RemoteAnimationFinishedStub(BackAnimationRunner runner) { + mRunnerRef = new WeakReference<>(runner); + } + + @Override + public void onAnimationFinished() { + BackAnimationRunner runner = mRunnerRef.get(); + if (runner == null) { + return; + } + if (runner.shouldMonitorCUJ(runner.mApps)) { + InteractionJankMonitor.getInstance().end(runner.mCujType); + } + + runner.mFinishedCallback.run(); + for (int i = runner.mApps.length - 1; i >= 0; --i) { + SurfaceControl sc = runner.mApps[i].leash; + if (sc != null && sc.isValid()) { + sc.release(); + } + } + runner.mApps = null; + runner.mFinishedCallback = null; + } + } + /** * Called from {@link IBackAnimationRunner}, it will deliver these * {@link RemoteAnimationTarget}s to the corresponding runner. @@ -108,16 +145,9 @@ public class BackAnimationRunner { void startAnimation(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, Runnable finishedCallback) { InteractionJankMonitor interactionJankMonitor = InteractionJankMonitor.getInstance(); - final IRemoteAnimationFinishedCallback callback = - new IRemoteAnimationFinishedCallback.Stub() { - @Override - public void onAnimationFinished() { - if (shouldMonitorCUJ(apps)) { - interactionJankMonitor.end(mCujType); - } - finishedCallback.run(); - } - }; + mFinishedCallback = finishedCallback; + mApps = apps; + if (mRemoteCallback == null) mRemoteCallback = new RemoteAnimationFinishedStub(this); mWaitingAnimation = false; if (shouldMonitorCUJ(apps)) { interactionJankMonitor.begin( @@ -125,7 +155,7 @@ public class BackAnimationRunner { } try { getRunner().onAnimationStart(TRANSIT_OLD_UNSET, apps, wallpapers, - nonApps, callback); + nonApps, mRemoteCallback); } catch (RemoteException e) { Log.w(TAG, "Failed call onAnimationStart", e); } 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 03a851bb9507..4c2588984500 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 @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.app.KeyguardManager; import android.content.Context; import android.content.pm.LauncherApps; +import android.hardware.input.InputManager; import android.os.Handler; import android.os.UserManager; import android.view.Choreographer; @@ -266,7 +267,8 @@ public abstract class WMShellModule { AppHandleEducationController appHandleEducationController, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + DesktopModeEventLogger desktopModeEventLogger) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return new DesktopModeWindowDecorViewModel( context, @@ -294,7 +296,8 @@ public abstract class WMShellModule { appHandleEducationController, windowDecorCaptionHandleRepository, desktopActivityOrientationHandler, - focusTransitionObserver); + focusTransitionObserver, + desktopModeEventLogger); } return new CaptionWindowDecorViewModel( context, @@ -644,7 +647,10 @@ public abstract class WMShellModule { @ShellMainThread Handler mainHandler, Optional<DesktopTasksLimiter> desktopTasksLimiter, Optional<RecentTasksController> recentTasksController, - InteractionJankMonitor interactionJankMonitor) { + InteractionJankMonitor interactionJankMonitor, + InputManager inputManager, + FocusTransitionObserver focusTransitionObserver, + DesktopModeEventLogger desktopModeEventLogger) { return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController, displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, dragAndDropController, transitions, keyguardManager, @@ -655,7 +661,9 @@ public abstract class WMShellModule { desktopRepository, desktopModeLoggerTransitionObserver, launchAdjacentController, recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter, - recentTasksController.orElse(null), interactionJankMonitor, mainHandler); + recentTasksController.orElse(null), interactionJankMonitor, mainHandler, + inputManager, focusTransitionObserver, + desktopModeEventLogger); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt index 320c003f41eb..9d4926b47def 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt @@ -71,7 +71,7 @@ class DesktopFullImmersiveTransitionHandler( /** Whether there is an immersive transition that hasn't completed yet. */ private val inProgress: Boolean - get() = state != null + get() = state != null || pendingExternalExitTransitions.isNotEmpty() private val rectEvaluator = RectEvaluator() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt index 435019929cbd..df9fc59b925e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt @@ -58,9 +58,9 @@ class DesktopMixedTransitionHandler( freeformTaskTransitionHandler.startMinimizedModeTransition(wct) /** Starts close transition and handles or delegates desktop task close animation. */ - override fun startRemoveTransition(wct: WindowContainerTransaction?) { + override fun startRemoveTransition(wct: WindowContainerTransaction?): IBinder { requireNotNull(wct) - transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, /* handler= */ this) + return transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, /* handler= */ this) } /** Returns null, as it only handles transitions started from Shell. */ 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 8ebe503a3816..255ca6e2511f 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 @@ -16,11 +16,20 @@ package com.android.wm.shell.desktopmode +import android.app.ActivityManager.RunningTaskInfo +import android.util.Size +import android.view.InputDevice.SOURCE_MOUSE +import android.view.InputDevice.SOURCE_TOUCHSCREEN +import android.view.MotionEvent +import android.view.MotionEvent.TOOL_TYPE_FINGER +import android.view.MotionEvent.TOOL_TYPE_MOUSE +import android.view.MotionEvent.TOOL_TYPE_STYLUS import com.android.internal.annotations.VisibleForTesting import com.android.internal.protolog.ProtoLog import com.android.internal.util.FrameworkStatsLog import com.android.window.flags.Flags import com.android.wm.shell.EventLogTags +import com.android.wm.shell.common.DisplayController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import java.security.SecureRandom import java.util.Random @@ -176,7 +185,13 @@ class DesktopModeEventLogger { * Logs that a task resize event is starting with [taskSizeUpdate] within a Desktop mode * session. */ - fun logTaskResizingStarted(taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingStarted( + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, + taskInfo: RunningTaskInfo, + displayController: DisplayController? = null, + displayLayoutSize: Size? = null, + ) { if (!Flags.enableResizingMetrics()) return val sessionId = currentSessionId.get() @@ -188,11 +203,19 @@ class DesktopModeEventLogger { return } + val taskSizeUpdate = createTaskSizeUpdate( + resizeTrigger, + motionEvent, + taskInfo, + displayController = displayController, + displayLayoutSize = displayLayoutSize, + ) + ProtoLog.v( WM_SHELL_DESKTOP_MODE, - "DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s", + "DesktopModeLogger: Logging task resize is starting, session: %s, taskSizeUpdate: %s", sessionId, - taskSizeUpdate.instanceId + taskSizeUpdate ) logTaskSizeUpdated( FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE, @@ -203,7 +226,15 @@ class DesktopModeEventLogger { /** * Logs that a task resize event is ending with [taskSizeUpdate] within a Desktop mode session. */ - fun logTaskResizingEnded(taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingEnded( + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, + taskInfo: RunningTaskInfo, + taskHeight: Int? = null, + taskWidth: Int? = null, + displayController: DisplayController? = null, + displayLayoutSize: Size? = null, + ) { if (!Flags.enableResizingMetrics()) return val sessionId = currentSessionId.get() @@ -215,18 +246,61 @@ class DesktopModeEventLogger { return } + val taskSizeUpdate = createTaskSizeUpdate( + resizeTrigger, + motionEvent, + taskInfo, + taskHeight, + taskWidth, + displayController, + displayLayoutSize, + ) + ProtoLog.v( WM_SHELL_DESKTOP_MODE, - "DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s", + "DesktopModeLogger: Logging task resize is ending, session: %s, taskSizeUpdate: %s", sessionId, - taskSizeUpdate.instanceId + taskSizeUpdate ) + logTaskSizeUpdated( FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE, sessionId, taskSizeUpdate ) } + private fun createTaskSizeUpdate( + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, + taskInfo: RunningTaskInfo, + taskHeight: Int? = null, + taskWidth: Int? = null, + displayController: DisplayController? = null, + displayLayoutSize: Size? = null, + ): TaskSizeUpdate { + val taskBounds = taskInfo.configuration.windowConfiguration.bounds + + val height = taskHeight ?: taskBounds.height() + val width = taskWidth ?: taskBounds.width() + + val displaySize = when { + displayLayoutSize != null -> displayLayoutSize.height * displayLayoutSize.width + displayController != null -> displayController.getDisplayLayout(taskInfo.displayId) + ?.let { it.height() * it.width() } + else -> null + } + + return TaskSizeUpdate( + resizeTrigger, + getInputMethodFromMotionEvent(motionEvent), + taskInfo.taskId, + taskInfo.effectiveUid, + height, + width, + displaySize, + ) + } + fun logTaskInfoStateInit() { logTaskUpdate( FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD, @@ -238,7 +312,8 @@ class DesktopModeEventLogger { taskHeight = 0, taskWidth = 0, taskX = 0, - taskY = 0) + taskY = 0 + ) ) } @@ -314,7 +389,7 @@ class DesktopModeEventLogger { /* task_width */ taskSizeUpdate.taskWidth, /* display_area */ - taskSizeUpdate.displayArea + taskSizeUpdate.displayArea ?: -1 ) } @@ -364,9 +439,24 @@ class DesktopModeEventLogger { val uid: Int, val taskHeight: Int, val taskWidth: Int, - val displayArea: Int, + val displayArea: Int?, ) + private fun getInputMethodFromMotionEvent(e: MotionEvent?): InputMethod { + if (e == null) return InputMethod.UNKNOWN_INPUT_METHOD + + val toolType = e.getToolType( + e.findPointerIndex(e.getPointerId(0)) + ) + return when { + toolType == TOOL_TYPE_STYLUS -> InputMethod.STYLUS + toolType == TOOL_TYPE_MOUSE -> InputMethod.MOUSE + toolType == TOOL_TYPE_FINGER && e.source == SOURCE_MOUSE -> InputMethod.TOUCHPAD + toolType == TOOL_TYPE_FINGER && e.source == SOURCE_TOUCHSCREEN -> InputMethod.TOUCH + else -> InputMethod.UNKNOWN_INPUT_METHOD + } + } + // Default value used when the task was not minimized. @VisibleForTesting const val UNSET_MINIMIZE_REASON = @@ -499,6 +589,10 @@ class DesktopModeEventLogger { FrameworkStatsLog .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__SNAP_RIGHT_MENU_RESIZE_TRIGGER ), + MAXIMIZE_MENU( + FrameworkStatsLog + .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__MAXIMIZE_MENU_RESIZE_TRIGGER + ), } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt index eeb7ac852070..85a3126d9de6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt @@ -30,7 +30,6 @@ import androidx.core.util.valueIterator import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository -import com.android.wm.shell.desktopmode.persistence.DesktopTask import com.android.wm.shell.desktopmode.persistence.DesktopTaskState import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.annotations.ShellMainThread @@ -124,7 +123,8 @@ class DesktopRepository ( if (!Flags.enableDesktopWindowingPersistence()) return // TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized mainCoroutineScope.launch { - val desktop = persistentRepository.readDesktop() + val desktop = persistentRepository.readDesktop() ?: return@launch + val maxTasks = DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 } ?: desktop.zOrderedTasksCount @@ -132,13 +132,11 @@ class DesktopRepository ( desktop.zOrderedTasksList // Reverse it so we initialize the repo from bottom to top. .reversed() - .map { taskId -> - desktop.tasksByTaskIdMap.getOrDefault( - taskId, - DesktopTask.getDefaultInstance() - ) + .mapNotNull { taskId -> + desktop.tasksByTaskIdMap[taskId]?.takeIf { + it.desktopTaskState == DesktopTaskState.VISIBLE + } } - .filter { task -> task.desktopTaskState == DesktopTaskState.VISIBLE } .take(maxTasks) .forEach { task -> addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId) @@ -522,6 +520,7 @@ class DesktopRepository ( "${innerPrefix}freeformTasksInZOrder=${data.freeformTasksInZOrder.toDumpString()}" ) pw.println("${innerPrefix}minimizedTasks=${data.minimizedTasks.toDumpString()}") + pw.println("${innerPrefix}fullImmersiveTaskId=${data.fullImmersiveTaskId}") } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 29e302a0f0cc..69776cd4740a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -35,6 +35,9 @@ import android.graphics.Point import android.graphics.PointF import android.graphics.Rect import android.graphics.Region +import android.hardware.input.InputManager +import android.hardware.input.InputManager.KeyGestureEventHandler +import android.hardware.input.KeyGestureEvent import android.os.Binder import android.os.Handler import android.os.IBinder @@ -42,6 +45,8 @@ import android.os.SystemProperties import android.util.Size import android.view.Display.DEFAULT_DISPLAY import android.view.DragEvent +import android.view.KeyEvent +import android.view.MotionEvent import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_CLOSE @@ -57,6 +62,7 @@ import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import androidx.annotation.BinderThread +import com.android.hardware.input.Flags.useKeyGestureEventHandler import com.android.internal.annotations.VisibleForTesting import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE @@ -65,6 +71,7 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.internal.policy.ScreenDecorationsUtils import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags +import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController @@ -78,12 +85,13 @@ import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SingleInstanceRemoteListener import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing -import com.android.wm.shell.desktopmode.DesktopRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType +import com.android.wm.shell.desktopmode.DesktopRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler import com.android.wm.shell.draganddrop.DragAndDropController +import com.android.wm.shell.freeform.FreeformTaskTransitionStarter import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.recents.RecentTasksController import com.android.wm.shell.recents.RecentsTransitionHandler @@ -92,7 +100,6 @@ import com.android.wm.shell.shared.ShellSharedConstants import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.shared.annotations.ExternalThread import com.android.wm.shell.shared.annotations.ShellMainThread -import com.android.wm.shell.freeform.FreeformTaskTransitionStarter import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity @@ -105,6 +112,7 @@ import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.UserChangeListener +import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility @@ -118,7 +126,7 @@ import java.io.PrintWriter import java.util.Optional import java.util.concurrent.Executor import java.util.function.Consumer - +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger /** Handles moving tasks in and out of desktop */ class DesktopTasksController( private val context: Context, @@ -149,11 +157,15 @@ class DesktopTasksController( private val recentTasksController: RecentTasksController?, private val interactionJankMonitor: InteractionJankMonitor, @ShellMainThread private val handler: Handler, + private val inputManager: InputManager, + private val focusTransitionObserver: FocusTransitionObserver, + private val desktopModeEventLogger: DesktopModeEventLogger, ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler, DragAndDropController.DragAndDropListener, - UserChangeListener { + UserChangeListener, + KeyGestureEventHandler { private val desktopMode: DesktopModeImpl private var visualIndicator: DesktopModeVisualIndicator? = null @@ -226,6 +238,9 @@ class DesktopTasksController( } ) dragAndDropController.addListener(this) + if (useKeyGestureEventHandler() && enableMoveToNextDisplayShortcut()) { + inputManager.registerKeyGestureEventHandler(this) + } } @VisibleForTesting @@ -409,7 +424,7 @@ class DesktopTasksController( interactionJankMonitor.begin(taskSurface, context, handler, CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD) dragToDesktopTransitionHandler.startDragToDesktopTransition( - taskInfo, + taskInfo.taskId, dragToDesktopValueAnimator ) } @@ -461,7 +476,12 @@ class DesktopTasksController( * @param displayId display id of the window that's being closed * @param taskId task id of the window that's being closed */ - fun onDesktopWindowClose(wct: WindowContainerTransaction, displayId: Int, taskId: Int) { + fun onDesktopWindowClose( + wct: WindowContainerTransaction, + displayId: Int, + taskInfo: RunningTaskInfo, + ): ((IBinder) -> Unit)? { + val taskId = taskInfo.taskId if (taskRepository.isOnlyVisibleNonClosingTask(taskId)) { removeWallpaperActivity(wct) } @@ -472,6 +492,7 @@ class DesktopTasksController( taskId ) ) + return immersiveTransitionHandler.exitImmersiveIfApplicable(wct, taskInfo) } fun minimizeTask(taskInfo: RunningTaskInfo) { @@ -728,7 +749,11 @@ class DesktopTasksController( * bounds) and a free floating state (either the last saved bounds if available or the default * bounds otherwise). */ - fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) { + fun toggleDesktopTaskSize( + taskInfo: RunningTaskInfo, + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, + ) { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return val stableBounds = Rect().apply { displayLayout.getStableBounds(this) } @@ -775,7 +800,10 @@ class DesktopTasksController( taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding) val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) - + desktopModeEventLogger.logTaskResizingEnded( + resizeTrigger, motionEvent, taskInfo, destinationBounds.height(), + destinationBounds.width(), displayController + ) toggleResizeDesktopTaskTransitionHandler.startTransition(wct) } @@ -865,9 +893,19 @@ class DesktopTasksController( taskInfo: RunningTaskInfo, taskSurface: SurfaceControl, currentDragBounds: Rect, - position: SnapPosition + position: SnapPosition, + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, ) { val destinationBounds = getSnapBounds(taskInfo, position) + desktopModeEventLogger.logTaskResizingEnded( + resizeTrigger, + motionEvent, + taskInfo, + destinationBounds.height(), + destinationBounds.width(), + displayController, + ) if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) { // Handle the case where we attempt to snap resize when already snap resized: the task // position won't need to change but we want to animate the surface going back to the @@ -896,7 +934,8 @@ class DesktopTasksController( position: SnapPosition, taskSurface: SurfaceControl, currentDragBounds: Rect, - dragStartBounds: Rect + dragStartBounds: Rect, + motionEvent: MotionEvent, ) { releaseVisualIndicator() if (!taskInfo.isResizeable && DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE.isTrue()) { @@ -913,10 +952,25 @@ class DesktopTasksController( isResizable = taskInfo.isResizeable, ) } else { + val resizeTrigger = if (position == SnapPosition.LEFT) { + ResizeTrigger.DRAG_LEFT + } else { + ResizeTrigger.DRAG_RIGHT + } + desktopModeEventLogger.logTaskResizingStarted( + resizeTrigger, motionEvent, taskInfo, displayController + ) interactionJankMonitor.begin( taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable" ) - snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position) + snapToHalfScreen( + taskInfo, + taskSurface, + currentDragBounds, + position, + resizeTrigger, + motionEvent, + ) } } @@ -1581,12 +1635,26 @@ class DesktopTasksController( getFocusedFreeformTask(displayId)?.let { requestSplit(it, leftOrTop) } } + /** Move the focused desktop task in given `displayId` to next display. */ + fun moveFocusedTaskToNextDisplay(displayId: Int) { + getFocusedFreeformTask(displayId)?.let { moveToNextDisplay(it.taskId) } + } + private fun getFocusedFreeformTask(displayId: Int): RunningTaskInfo? { return shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo -> taskInfo.isFocused && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM } } + // TODO(b/364154795): wait for the completion of moveToNextDisplay transition, otherwise it will + // pick a wrong task when a user quickly perform other actions with keyboard shortcuts after + // moveToNextDisplay. + private fun getGloballyFocusedFreeformTask(): RunningTaskInfo? = + shellTaskOrganizer.getRunningTasks().find { taskInfo -> + taskInfo.windowingMode == WINDOWING_MODE_FREEFORM && + focusTransitionObserver.hasGlobalFocus(taskInfo) + } + /** * Requests a task be transitioned from desktop to split select. Applies needed windowing * changes if this transition is enabled. @@ -1702,6 +1770,7 @@ class DesktopTasksController( currentDragBounds: Rect, validDragArea: Rect, dragStartBounds: Rect, + motionEvent: MotionEvent, ) { if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) { return @@ -1722,12 +1791,22 @@ class DesktopTasksController( } IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { handleSnapResizingTask( - taskInfo, SnapPosition.LEFT, taskSurface, currentDragBounds, dragStartBounds + taskInfo, + SnapPosition.LEFT, + taskSurface, + currentDragBounds, + dragStartBounds, + motionEvent, ) } IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { handleSnapResizingTask( - taskInfo, SnapPosition.RIGHT, taskSurface, currentDragBounds, dragStartBounds + taskInfo, + SnapPosition.RIGHT, + taskSurface, + currentDragBounds, + dragStartBounds, + motionEvent, ) } IndicatorType.NO_INDICATOR -> { @@ -1941,6 +2020,31 @@ class DesktopTasksController( taskRepository.dump(pw, innerPrefix) } + override fun handleKeyGestureEvent( + event: KeyGestureEvent, + focusedToken: IBinder? + ): Boolean { + if (!isKeyGestureSupported(event.keyGestureType)) return false + when (event.keyGestureType) { + KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY -> { + if (event.keycodes.contains(KeyEvent.KEYCODE_D) && + event.hasModifiers(KeyEvent.META_CTRL_ON or KeyEvent.META_META_ON)) { + logV("Key gesture MOVE_TO_NEXT_DISPLAY is handled") + getGloballyFocusedFreeformTask()?.let { moveToNextDisplay(it.taskId) } + return true + } + return false + } + else -> return false + } + } + + override fun isKeyGestureSupported(gestureType: Int): Boolean = when (gestureType) { + KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY + -> enableMoveToNextDisplayShortcut() + else -> false + } + /** The interface for calls from outside the shell, within the host process. */ @ExternalThread private inner class DesktopModeImpl : DesktopMode { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt index 34c2f1e760a2..d7d55195d4cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt @@ -109,8 +109,8 @@ sealed class DragToDesktopTransitionHandler( * after one of the "end" or "cancel" transitions is merged into this transition. */ fun startDragToDesktopTransition( - taskInfo: RunningTaskInfo, - dragToDesktopAnimator: MoveToDesktopAnimator + taskId: Int, + dragToDesktopAnimator: MoveToDesktopAnimator, ) { if (inProgress) { ProtoLog.v( @@ -137,26 +137,23 @@ sealed class DragToDesktopTransitionHandler( ) val wct = WindowContainerTransaction() wct.sendPendingIntent(pendingIntent, launchHomeIntent, Bundle()) - // The home launch done above will result in an attempt to move the task to pip if - // applicable, resulting in a broken state. Prevent that here. - wct.setDoNotPip(taskInfo.token) val startTransitionToken = transitions.startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this) transitionState = - if (isSplitTask(taskInfo.taskId)) { + if (isSplitTask(taskId)) { val otherTask = - getOtherSplitTask(taskInfo.taskId) + getOtherSplitTask(taskId) ?: throw IllegalStateException("Expected split task to have a counterpart.") TransitionState.FromSplit( - draggedTaskId = taskInfo.taskId, + draggedTaskId = taskId, dragAnimator = dragToDesktopAnimator, startTransitionToken = startTransitionToken, otherSplitTask = otherTask ) } else { TransitionState.FromFullscreen( - draggedTaskId = taskInfo.taskId, + draggedTaskId = taskId, dragAnimator = dragToDesktopAnimator, startTransitionToken = startTransitionToken ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt index 3f41d7cf4e86..2d11e02bd3c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt @@ -73,15 +73,14 @@ class DesktopPersistentRepository( */ private suspend fun getDesktopRepositoryState( userId: Int = DEFAULT_USER_ID - ): DesktopRepositoryState = + ): DesktopRepositoryState? = try { dataStoreFlow .first() - .desktopRepoByUserMap - .getOrDefault(userId, DesktopRepositoryState.getDefaultInstance()) + .desktopRepoByUserMap[userId] } catch (e: Exception) { Log.e(TAG, "Unable to read from datastore", e) - DesktopRepositoryState.getDefaultInstance() + null } /** @@ -91,13 +90,13 @@ class DesktopPersistentRepository( suspend fun readDesktop( userId: Int = DEFAULT_USER_ID, desktopId: Int = DEFAULT_DESKTOP_ID, - ): Desktop = + ): Desktop? = try { val repository = getDesktopRepositoryState(userId) - repository.getDesktopOrThrow(desktopId) + repository?.getDesktopOrThrow(desktopId) } catch (e: Exception) { Log.e(TAG, "Unable to get desktop info from persistent repository", e) - Desktop.getDefaultInstance() + null } /** Adds or updates a desktop stored in the datastore */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java index 6aaf001d46f3..58337ece0991 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java @@ -99,9 +99,11 @@ public class FreeformTaskTransitionHandler @Override - public void startRemoveTransition(WindowContainerTransaction wct) { + public IBinder startRemoveTransition(WindowContainerTransaction wct) { final int type = WindowManager.TRANSIT_CLOSE; - mPendingTransitionTokens.add(mTransitions.startTransition(type, wct, this)); + final IBinder transition = mTransitions.startTransition(type, wct, this); + mPendingTransitionTokens.add(transition); + return transition; } @Override @@ -229,8 +231,7 @@ public class FreeformTaskTransitionHandler SurfaceControl.Transaction t = new SurfaceControl.Transaction(); SurfaceControl sc = change.getLeash(); finishT.hide(sc); - Rect startBounds = new Rect(change.getTaskInfo().configuration.windowConfiguration - .getBounds()); + final Rect startBounds = new Rect(change.getStartAbsBounds()); animator.addUpdateListener(animation -> { t.setPosition(sc, startBounds.left, startBounds.top + (animation.getAnimatedFraction() * screenHeight)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java index ea68a694c3b9..5984d486f838 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java @@ -48,6 +48,7 @@ public interface FreeformTaskTransitionStarter { * * @param wct the {@link WindowContainerTransaction} that closes the task * + * @return the started transition */ - void startRemoveTransition(WindowContainerTransaction wct); + IBinder startRemoveTransition(WindowContainerTransaction wct); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index b36b1f84d21f..3e6d36ce0ca3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -110,12 +110,12 @@ public interface SplitScreen { void registerSplitAnimationListener(@NonNull SplitInvocationListener listener, @NonNull Executor executor); - /** Called when device waking up finished. */ - void onFinishedWakingUp(); - /** Called when device starts going to sleep (screen off). */ void onStartedGoingToSleep(); + /** Called when device wakes up. */ + void onStartedWakingUp(); + /** Called when requested to go to fullscreen from the current active split app. */ void goToFullscreenFromSplit(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index a23b576beebc..6398d31b4f82 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -471,14 +471,14 @@ public class SplitScreenController implements SplitDragPolicy.Starter, mStageCoordinator.onKeyguardStateChanged(visible, occluded); } - public void onFinishedWakingUp() { - mStageCoordinator.onFinishedWakingUp(); - } - public void onStartedGoingToSleep() { mStageCoordinator.onStartedGoingToSleep(); } + public void onStartedWakingUp() { + mStageCoordinator.onStartedWakingUp(); + } + public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide); } @@ -1084,13 +1084,13 @@ public class SplitScreenController implements SplitDragPolicy.Starter, } @Override - public void onFinishedWakingUp() { - mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp); + public void onStartedGoingToSleep() { + mMainExecutor.execute(SplitScreenController.this::onStartedGoingToSleep); } @Override - public void onStartedGoingToSleep() { - mMainExecutor.execute(SplitScreenController.this::onStartedGoingToSleep); + public void onStartedWakingUp() { + mMainExecutor.execute(SplitScreenController.this::onStartedWakingUp); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 3e76403de51b..7893267d014a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -1138,14 +1138,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, "onKeyguardVisibilityChanged: active=%b occludingTaskRunning=%b", active, occludingTaskRunning); setDividerVisibility(!mKeyguardActive, null); - - if (active && occludingTaskRunning) { - dismissSplitKeepingLastActiveStage(EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); - } } - void onFinishedWakingUp() { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinishedWakingUp"); + void onStartedWakingUp() { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStartedWakingUp"); if (mBreakOnNextWake) { dismissSplitKeepingLastActiveStage(EXIT_REASON_DEVICE_FOLDED); } @@ -1908,60 +1904,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - /** Callback when split roots have child or haven't under it. - * NOTICE: This only be called on legacy transition. */ - @Override - public void onStageHasChildrenChanged(StageTaskListener stageListener) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b", - stageListener == mMainStage); - final boolean hasChildren = stageListener.mHasChildren; - final boolean isSideStage = stageListener == mSideStage; - if (!hasChildren && !mIsExiting && isSplitActive()) { - if (isSideStage && mMainStage.mVisible) { - // Exit to main stage if side stage no longer has children. - mSplitLayout.flingDividerToDismiss( - mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT, - EXIT_REASON_APP_FINISHED); - } else if (!isSideStage && mSideStage.mVisible) { - // Exit to side stage if main stage no longer has children. - mSplitLayout.flingDividerToDismiss( - mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT, - EXIT_REASON_APP_FINISHED); - } else if (!isSplitScreenVisible() && mSplitRequest == null) { - // Dismiss split screen in the background once any sides of the split become empty. - exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED); - } - } else if (isSideStage && hasChildren && !isSplitActive()) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareEnterSplitScreen(wct); - - mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> { - if (mIsDropEntering) { - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - mIsDropEntering = false; - mSkipEvictingMainStageChildren = false; - } else { - mShowDecorImmediately = true; - mSplitLayout.flingDividerToCenter(/*finishCallback*/ null); - } - }); - } - if (mMainStage.mHasChildren && mSideStage.mHasChildren) { - mShouldUpdateRecents = true; - clearRequestIfPresented(); - updateRecentTasksSplitPair(); - - if (!mLogger.hasStartedSession() && !mLogger.hasValidEnterSessionId()) { - mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE); - } - mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), - getMainStagePosition(), mMainStage.getTopChildTaskUid(), - getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLeftRightSplit()); - } - } - @Override public void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener, ActivityManager.RunningTaskInfo taskInfo) { @@ -2485,6 +2427,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int transitType = info.getType(); TransitionInfo.Change pipChange = null; int closingSplitTaskId = -1; + // This array tracks if we are sending stages TO_BACK in this transition. + // TODO (b/349828130): Update for n apps + boolean[] stagesSentToBack = new boolean[2]; + for (int iC = 0; iC < info.getChanges().size(); ++iC) { final TransitionInfo.Change change = info.getChanges().get(iC); if (change.getMode() == TRANSIT_CHANGE @@ -2552,23 +2498,31 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } continue; } + final int taskId = taskInfo.taskId; if (isOpeningType(change.getMode())) { - if (!stage.containsTask(taskInfo.taskId)) { + if (!stage.containsTask(taskId)) { Log.w(TAG, "Expected onTaskAppeared on " + stage + " to have been called" - + " with " + taskInfo.taskId + " before startAnimation()."); - record.addRecord(stage, true, taskInfo.taskId); + + " with " + taskId + " before startAnimation()."); + record.addRecord(stage, true, taskId); } } else if (change.getMode() == TRANSIT_CLOSE) { - if (stage.containsTask(taskInfo.taskId)) { - record.addRecord(stage, false, taskInfo.taskId); + if (stage.containsTask(taskId)) { + record.addRecord(stage, false, taskId); Log.w(TAG, "Expected onTaskVanished on " + stage + " to have been called" - + " with " + taskInfo.taskId + " before startAnimation()."); + + " with " + taskId + " before startAnimation()."); } } if (isClosingType(change.getMode()) && - getStageOfTask(change.getTaskInfo().taskId) != STAGE_TYPE_UNDEFINED) { - // If either one of the 2 stages is closing we're assuming we'll break split - closingSplitTaskId = change.getTaskInfo().taskId; + getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED) { + + // Record which stages are getting sent to back + if (change.getMode() == TRANSIT_TO_BACK) { + stagesSentToBack[getStageOfTask(taskId)] = true; + } + + // (For PiP transitions) If either one of the 2 stages is closing we're assuming + // we'll break split + closingSplitTaskId = taskId; } } @@ -2594,6 +2548,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return true; } + // If keyguard is active, check to see if we have our TO_BACK transitions in order. + // This array should either be all false (no split stages sent to back) or all true + // (all stages sent to back). In any other case (which can happen with SHOW_ABOVE_LOCKED + // apps) we should break split. + if (mKeyguardActive) { + boolean isFirstStageSentToBack = stagesSentToBack[0]; + for (boolean b : stagesSentToBack) { + // Compare each boolean to the first one. If any are different, break split. + if (b != isFirstStageSentToBack) { + dismissSplitKeepingLastActiveStage(EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); + break; + } + } + } + final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage(); if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0 || dismissStages.size() == 1) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 6313231b449e..b33f3e98f350 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -75,8 +75,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { public interface StageListenerCallbacks { void onRootTaskAppeared(); - void onStageHasChildrenChanged(StageTaskListener stageTaskListener); - void onStageVisibilityChanged(StageTaskListener stageTaskListener); void onChildTaskStatusChanged(StageTaskListener stage, int taskId, boolean present, @@ -207,7 +205,10 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { mIconProvider); mHasRootTask = true; mCallbacks.onRootTaskAppeared(); - sendStatusChanged(); + if (mVisible != mRootTaskInfo.isVisible) { + mVisible = mRootTaskInfo.isVisible; + mCallbacks.onStageVisibilityChanged(this); + } mSyncQueue.runInSync(t -> mDimLayer = SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer")); } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { @@ -498,22 +499,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { return true; } - private void sendStatusChanged() { - boolean hasChildren = mChildrenTaskInfo.size() > 0; - boolean visible = mRootTaskInfo.isVisible; - if (!mHasRootTask) return; - - if (mHasChildren != hasChildren) { - mHasChildren = hasChildren; - mCallbacks.onStageHasChildrenChanged(this); - } - - if (mVisible != visible) { - mVisible = visible; - mCallbacks.onStageVisibilityChanged(this); - } - } - @Override @CallSuper public void dump(@NonNull PrintWriter pw, String prefix) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 509cb85c96cd..fde01eefee17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -274,6 +274,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL closeDragResizeListener(); mDragResizeListener = new DragResizeInputListener( mContext, + mTaskInfo, mHandler, mChoreographer, mDisplay.getDisplayId(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 9e089b2460f6..a775cbc6c9f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -32,6 +32,7 @@ import static android.view.WindowInsets.Type.statusBars; import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU; import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing; +import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; @@ -56,6 +57,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; @@ -102,6 +104,7 @@ import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -133,6 +136,7 @@ import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; import kotlin.Pair; import kotlin.Unit; +import kotlin.jvm.functions.Function1; import kotlinx.coroutines.ExperimentalCoroutinesApi; @@ -219,6 +223,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, }; private final TaskPositionerFactory mTaskPositionerFactory; private final FocusTransitionObserver mFocusTransitionObserver; + private final DesktopModeEventLogger mDesktopModeEventLogger; public DesktopModeWindowDecorViewModel( Context context, @@ -246,7 +251,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, AppHandleEducationController appHandleEducationController, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + DesktopModeEventLogger desktopModeEventLogger) { this( context, shellExecutor, @@ -279,7 +285,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, new TaskPositionerFactory(), - focusTransitionObserver); + focusTransitionObserver, + desktopModeEventLogger); } @VisibleForTesting @@ -315,7 +322,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, TaskPositionerFactory taskPositionerFactory, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + DesktopModeEventLogger desktopModeEventLogger) { mContext = context; mMainExecutor = shellExecutor; mMainHandler = mainHandler; @@ -376,6 +384,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, }; mTaskPositionerFactory = taskPositionerFactory; mFocusTransitionObserver = focusTransitionObserver; + mDesktopModeEventLogger = desktopModeEventLogger; shellInit.addInitCallback(this::onInit, this); } @@ -545,15 +554,20 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, >= MANAGE_WINDOWS_MINIMUM_INSTANCES); } - private void onMaximizeOrRestore(int taskId, String source) { + private void onMaximizeOrRestore(int taskId, String source, ResizeTrigger resizeTrigger, + MotionEvent motionEvent) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); if (decoration == null) { return; } + mDesktopModeEventLogger.logTaskResizingStarted(resizeTrigger, motionEvent, + decoration.mTaskInfo, + mDisplayController, /* displayLayoutSize= */ null); mInteractionJankMonitor.begin( decoration.mTaskSurface, mContext, mMainHandler, Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source); - mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo); + mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo, resizeTrigger, + motionEvent); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } @@ -566,7 +580,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTasksController.toggleDesktopTaskFullImmersiveState(decoration.mTaskInfo); } - private void onSnapResize(int taskId, boolean left) { + private void onSnapResize(int taskId, boolean left, MotionEvent motionEvent) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); if (decoration == null) { return; @@ -577,13 +591,20 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Toast.makeText(mContext, R.string.desktop_mode_non_resizable_snap_text, Toast.LENGTH_SHORT).show(); } else { + ResizeTrigger resizeTrigger = + left ? ResizeTrigger.SNAP_LEFT_MENU : ResizeTrigger.SNAP_RIGHT_MENU; + mDesktopModeEventLogger.logTaskResizingStarted(resizeTrigger, motionEvent, + decoration.mTaskInfo, + mDisplayController, /* displayLayoutSize= */ null); mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, mMainHandler, Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable"); mDesktopTasksController.snapToHalfScreen( decoration.mTaskInfo, decoration.mTaskSurface, decoration.mTaskInfo.configuration.windowConfiguration.getBounds(), - left ? SnapPosition.LEFT : SnapPosition.RIGHT); + left ? SnapPosition.LEFT : SnapPosition.RIGHT, + resizeTrigger, + motionEvent); } decoration.closeHandleMenu(); @@ -735,6 +756,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private boolean mTouchscreenInUse; private boolean mHasLongClicked; private int mDragPointerId = -1; + private MotionEvent mMotionEvent; private DesktopModeTouchEventListener( RunningTaskInfo taskInfo, @@ -767,8 +789,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, SplitScreenController.EXIT_REASON_DESKTOP_MODE); } else { WindowContainerTransaction wct = new WindowContainerTransaction(); - mDesktopTasksController.onDesktopWindowClose(wct, mDisplayId, mTaskId); - mTaskOperations.closeTask(mTaskToken, wct); + final Function1<IBinder, Unit> runOnTransitionStart = + mDesktopTasksController.onDesktopWindowClose( + wct, mDisplayId, decoration.mTaskInfo); + final IBinder transition = mTaskOperations.closeTask(mTaskToken, wct); + if (transition != null && runOnTransitionStart != null) { + runOnTransitionStart.invoke(transition); + } } } else if (id == R.id.back_button) { mTaskOperations.injectBackKey(mDisplayId); @@ -791,7 +818,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } else { // Full immersive is disabled or task doesn't request/support it, so just // toggle between maximize/restore states. - onMaximizeOrRestore(decoration.mTaskInfo.taskId, "caption_bar_button"); + onMaximizeOrRestore(decoration.mTaskInfo.taskId, "caption_bar_button", + ResizeTrigger.MAXIMIZE_BUTTON, mMotionEvent); } } else if (id == R.id.minimize_window) { mDesktopTasksController.minimizeTask(decoration.mTaskInfo); @@ -800,6 +828,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, @Override public boolean onTouch(View v, MotionEvent e) { + mMotionEvent = e; final int id = v.getId(); final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); if ((e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN) { @@ -890,6 +919,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, */ @Override public boolean onGenericMotion(View v, MotionEvent ev) { + mMotionEvent = ev; final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final int id = v.getId(); if (ev.getAction() == ACTION_HOVER_ENTER && id == R.id.maximize_window) { @@ -1033,7 +1063,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, taskInfo, decoration.mTaskSurface, position, new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)), newTaskBounds, decoration.calculateValidDragArea(), - new Rect(mOnDragStartInitialBounds)); + new Rect(mOnDragStartInitialBounds), e); if (touchingButton && !mHasLongClicked) { // We need the input event to not be consumed here to end the ripple // effect on the touched button. We will reset drag state in the ensuing @@ -1080,7 +1110,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // Disallow double-tap to resize when in full immersive. return false; } - onMaximizeOrRestore(mTaskId, "double_tap"); + onMaximizeOrRestore(mTaskId, "double_tap", ResizeTrigger.DOUBLE_TAP_APP_HEADER, e); return true; } } @@ -1477,7 +1507,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mGenericLinksParser, mAssistContentRequester, mMultiInstanceHelper, - mWindowDecorCaptionHandleRepository); + mWindowDecorCaptionHandleRepository, + mDesktopModeEventLogger); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); final TaskPositioner taskPositioner = mTaskPositionerFactory.create( @@ -1494,15 +1525,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, final DesktopModeTouchEventListener touchEventListener = new DesktopModeTouchEventListener(taskInfo, taskPositioner); windowDecoration.setOnMaximizeOrRestoreClickListener(() -> { - onMaximizeOrRestore(taskInfo.taskId, "maximize_menu"); + onMaximizeOrRestore(taskInfo.taskId, "maximize_menu", ResizeTrigger.MAXIMIZE_MENU, + touchEventListener.mMotionEvent); return Unit.INSTANCE; }); windowDecoration.setOnLeftSnapClickListener(() -> { - onSnapResize(taskInfo.taskId, true /* isLeft */); + onSnapResize(taskInfo.taskId, /* isLeft= */ true, touchEventListener.mMotionEvent); return Unit.INSTANCE; }); windowDecoration.setOnRightSnapClickListener(() -> { - onSnapResize(taskInfo.taskId, false /* isLeft */); + onSnapResize(taskInfo.taskId, /* isLeft= */ false, touchEventListener.mMotionEvent); return Unit.INSTANCE; }); windowDecoration.setOnToDesktopClickListener(desktopModeTransitionSource -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 6eb20b9e3ae5..d94f3a9a70c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -94,6 +94,7 @@ import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.CaptionState; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; @@ -216,7 +217,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, - WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) { + WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, + DesktopModeEventLogger desktopModeEventLogger) { this (context, userContext, displayController, splitScreenController, desktopRepository, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser, @@ -227,7 +229,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper, - windowDecorCaptionHandleRepository); + windowDecorCaptionHandleRepository, desktopModeEventLogger); } DesktopModeWindowDecoration( @@ -256,11 +258,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin MaximizeMenuFactory maximizeMenuFactory, HandleMenuFactory handleMenuFactory, MultiInstanceHelper multiInstanceHelper, - WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) { + WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, + DesktopModeEventLogger desktopModeEventLogger) { super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, - surfaceControlViewHostFactory); + surfaceControlViewHostFactory, desktopModeEventLogger); mSplitScreenController = splitScreenController; mHandler = handler; mBgExecutor = bgExecutor; @@ -605,6 +608,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Trace.beginSection("DesktopModeWindowDecoration#relayout-DragResizeInputListener"); mDragResizeListener = new DragResizeInputListener( mContext, + mTaskInfo, mHandler, mChoreographer, mDisplay.getDisplayId(), @@ -612,7 +616,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mDragPositioningCallback, mSurfaceControlBuilderSupplier, mSurfaceControlTransactionSupplier, - mDisplayController); + mDisplayController, + mDesktopModeEventLogger); Trace.endSection(); } @@ -1700,7 +1705,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, - WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) { + WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, + DesktopModeEventLogger desktopModeEventLogger) { return new DesktopModeWindowDecoration( context, userContext, @@ -1719,7 +1725,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin genericLinksParser, assistContentRequester, multiInstanceHelper, - windowDecorCaptionHandleRepository); + windowDecorCaptionHandleRepository, + desktopModeEventLogger); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 4ff394e2b1a9..420409705b05 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -29,10 +29,12 @@ import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT; import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT; import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP; +import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen; import android.annotation.NonNull; +import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; @@ -59,6 +61,7 @@ import android.window.InputTransferToken; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import java.util.function.Consumer; import java.util.function.Supplier; @@ -83,14 +86,17 @@ class DragResizeInputListener implements AutoCloseable { private final TaskResizeInputEventReceiver mInputEventReceiver; private final Context mContext; + private final RunningTaskInfo mTaskInfo; private final SurfaceControl mInputSinkSurface; private final IBinder mSinkClientToken; private final InputChannel mSinkInputChannel; private final DisplayController mDisplayController; + private final DesktopModeEventLogger mDesktopModeEventLogger; private final Region mTouchRegion = new Region(); DragResizeInputListener( Context context, + RunningTaskInfo taskInfo, Handler handler, Choreographer choreographer, int displayId, @@ -98,12 +104,15 @@ class DragResizeInputListener implements AutoCloseable { DragPositioningCallback callback, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, - DisplayController displayController) { + DisplayController displayController, + DesktopModeEventLogger desktopModeEventLogger) { mContext = context; + mTaskInfo = taskInfo; mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; mDisplayId = displayId; mDecorationSurface = decorationSurface; mDisplayController = displayController; + mDesktopModeEventLogger = desktopModeEventLogger; mClientToken = new Binder(); final InputTransferToken inputTransferToken = new InputTransferToken(); mInputChannel = new InputChannel(); @@ -125,11 +134,12 @@ class DragResizeInputListener implements AutoCloseable { e.rethrowFromSystemServer(); } - mInputEventReceiver = new TaskResizeInputEventReceiver(context, mInputChannel, callback, + mInputEventReceiver = new TaskResizeInputEventReceiver(context, mTaskInfo, mInputChannel, + callback, handler, choreographer, () -> { final DisplayLayout layout = mDisplayController.getDisplayLayout(mDisplayId); return new Size(layout.width(), layout.height()); - }, this::updateSinkInputChannel); + }, this::updateSinkInputChannel, mDesktopModeEventLogger); mInputEventReceiver.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop()); mInputSinkSurface = surfaceControlBuilderSupplier.get() @@ -163,6 +173,22 @@ class DragResizeInputListener implements AutoCloseable { } } + DragResizeInputListener( + Context context, + RunningTaskInfo taskInfo, + Handler handler, + Choreographer choreographer, + int displayId, + SurfaceControl decorationSurface, + DragPositioningCallback callback, + Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, + Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, + DisplayController displayController) { + this(context, taskInfo, handler, choreographer, displayId, decorationSurface, callback, + surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, displayController, + new DesktopModeEventLogger()); + } + /** * Updates the geometry (the touch region) of this drag resize handler. * @@ -274,6 +300,7 @@ class DragResizeInputListener implements AutoCloseable { private static class TaskResizeInputEventReceiver extends InputEventReceiver implements DragDetector.MotionEventHandler { @NonNull private final Context mContext; + @NonNull private final RunningTaskInfo mTaskInfo; private final InputManager mInputManager; @NonNull private final InputChannel mInputChannel; @NonNull private final DragPositioningCallback mCallback; @@ -282,6 +309,7 @@ class DragResizeInputListener implements AutoCloseable { @NonNull private final DragDetector mDragDetector; @NonNull private final Supplier<Size> mDisplayLayoutSizeSupplier; @NonNull private final Consumer<Region> mTouchRegionConsumer; + @NonNull private final DesktopModeEventLogger mDesktopModeEventLogger; private final Rect mTmpRect = new Rect(); private boolean mConsumeBatchEventScheduled; private DragResizeWindowGeometry mDragResizeWindowGeometry; @@ -293,15 +321,24 @@ class DragResizeInputListener implements AutoCloseable { // resize events. For example, if multiple fingers are touching the screen, then each one // has a separate pointer id, but we only accept drag input from one. private int mDragPointerId = -1; + // The type of resizing that is currently being done. Used to track the same resize trigger + // on start and end of the resizing action. + private ResizeTrigger mResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER; + // The last MotionEvent on ACTION_DOWN, used to track the input tool type and source for + // logging the start and end of the resizing action. + private MotionEvent mLastMotionEventOnDown; private TaskResizeInputEventReceiver(@NonNull Context context, + @NonNull RunningTaskInfo taskInfo, @NonNull InputChannel inputChannel, @NonNull DragPositioningCallback callback, @NonNull Handler handler, @NonNull Choreographer choreographer, @NonNull Supplier<Size> displayLayoutSizeSupplier, - @NonNull Consumer<Region> touchRegionConsumer) { + @NonNull Consumer<Region> touchRegionConsumer, + @NonNull DesktopModeEventLogger desktopModeEventLogger) { super(inputChannel, handler.getLooper()); mContext = context; + mTaskInfo = taskInfo; mInputManager = context.getSystemService(InputManager.class); mInputChannel = inputChannel; mCallback = callback; @@ -322,6 +359,7 @@ class DragResizeInputListener implements AutoCloseable { ViewConfiguration.get(mContext).getScaledTouchSlop()); mDisplayLayoutSizeSupplier = displayLayoutSizeSupplier; mTouchRegionConsumer = touchRegionConsumer; + mDesktopModeEventLogger = desktopModeEventLogger; } /** @@ -395,6 +433,7 @@ class DragResizeInputListener implements AutoCloseable { @Override public boolean handleMotionEvent(View v, MotionEvent e) { boolean result = false; + // Check if this is a touch event vs mouse event. // Touch events are tracked in four corners. Other events are tracked in resize edges. switch (e.getActionMasked()) { @@ -416,6 +455,13 @@ class DragResizeInputListener implements AutoCloseable { "%s: Handling action down, update ctrlType to %d", TAG, ctrlType); mDragStartTaskBounds = mCallback.onDragPositioningStart(ctrlType, rawX, rawY); + mLastMotionEventOnDown = e; + mResizeTrigger = (ctrlType == CTRL_TYPE_BOTTOM || ctrlType == CTRL_TYPE_TOP + || ctrlType == CTRL_TYPE_RIGHT || ctrlType == CTRL_TYPE_LEFT) + ? ResizeTrigger.EDGE : ResizeTrigger.CORNER; + mDesktopModeEventLogger.logTaskResizingStarted(mResizeTrigger, + e, mTaskInfo, /* displayController= */ null, + /* displayLayoutSize= */ mDisplayLayoutSizeSupplier.get()); // Increase the input sink region to cover the whole screen; this is to // prevent input and focus from going to other tasks during a drag resize. updateInputSinkRegionForDrag(mDragStartTaskBounds); @@ -464,6 +510,12 @@ class DragResizeInputListener implements AutoCloseable { if (taskBounds.equals(mDragStartTaskBounds)) { mTouchRegionConsumer.accept(mTouchRegion); } + + mDesktopModeEventLogger.logTaskResizingEnded(mResizeTrigger, + mLastMotionEventOnDown, mTaskInfo, taskBounds.height(), + taskBounds.width(), + /* displayController= */ null, + /* displayLayoutSize= */ mDisplayLayoutSizeSupplier.get()); } mShouldHandleEvents = false; mDragPointerId = -1; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java index 33d1c260cb84..844ceb304bde 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java @@ -181,7 +181,7 @@ final class DragResizeWindowGeometry { } private boolean isInEdgeResizeBounds(float x, float y) { - return calculateEdgeResizeCtrlType(x, y) != 0; + return calculateEdgeResizeCtrlType(x, y) != CTRL_TYPE_UNDEFINED; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java index 61b93932013c..bc85d2b40748 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java @@ -76,13 +76,14 @@ class TaskOperations { closeTask(taskToken, new WindowContainerTransaction()); } - void closeTask(WindowContainerToken taskToken, WindowContainerTransaction wct) { + IBinder closeTask(WindowContainerToken taskToken, WindowContainerTransaction wct) { wct.removeTask(taskToken); if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mTransitionStarter.startRemoveTransition(wct); + return mTransitionStarter.startRemoveTransition(wct); } else { mSyncQueue.queue(wct); } + return null; } IBinder minimizeTask(WindowContainerToken taskToken) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 6b3b357f2f7b..34cc0986c83f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -58,6 +58,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer; @@ -111,6 +112,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final Context mContext; final @NonNull Context mUserContext; final @NonNull DisplayController mDisplayController; + final @NonNull DesktopModeEventLogger mDesktopModeEventLogger; final ShellTaskOrganizer mTaskOrganizer; final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier; final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier; @@ -163,7 +165,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, - new SurfaceControlViewHostFactory() {}); + new SurfaceControlViewHostFactory() {}, new DesktopModeEventLogger()); } WindowDecoration( @@ -177,13 +179,16 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, - SurfaceControlViewHostFactory surfaceControlViewHostFactory) { + SurfaceControlViewHostFactory surfaceControlViewHostFactory, + @NonNull DesktopModeEventLogger desktopModeEventLogger + ) { mContext = context; mUserContext = userContext; mDisplayController = displayController; mTaskOrganizer = taskOrganizer; mTaskInfo = taskInfo; mTaskSurface = cloneSurfaceControl(taskSurface, surfaceControlSupplier); + mDesktopModeEventLogger = desktopModeEventLogger; mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier; mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; mWindowContainerTransactionSupplier = windowContainerTransactionSupplier; diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index b7ddfd1fc6eb..4fe66f3357a3 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -298,12 +298,18 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("MAXIMIZE_APP"), extractor = - TaggedScenarioExtractorBuilder() - .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) - .setTransitionMatcher( - TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ) - .build(), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + return transitions.filter { + it.type == TransitionType.DESKTOP_MODE_TOGGLE_RESIZE + } + } + } + ), assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf( AppLayerIncreasesInSize(DESKTOP_MODE_APP), @@ -316,12 +322,18 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("MAXIMIZE_APP_NON_RESIZABLE"), extractor = - TaggedScenarioExtractorBuilder() - .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) - .setTransitionMatcher( - TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ) - .build(), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + return transitions.filter { + it.type == TransitionType.DESKTOP_MODE_TOGGLE_RESIZE + } + } + } + ), assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java index e6bd05b82be9..f935ac76bbeb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java @@ -40,6 +40,8 @@ public final class TestRunningTaskInfoBuilder { private WindowContainerToken mToken = createMockWCToken(); private int mParentTaskId = INVALID_TASK_ID; + private int mUid = INVALID_TASK_ID; + private int mTaskId = INVALID_TASK_ID; private Intent mBaseIntent = new Intent(); private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD; private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED; @@ -73,6 +75,18 @@ public final class TestRunningTaskInfoBuilder { return this; } + /** Sets the task info's effective UID. */ + public TestRunningTaskInfoBuilder setUid(int uid) { + mUid = uid; + return this; + } + + /** Sets the task info's UID. */ + public TestRunningTaskInfoBuilder setTaskId(int taskId) { + mTaskId = taskId; + return this; + } + /** * Set {@link ActivityManager.RunningTaskInfo#baseIntent} for the task info, by default * an empty intent is assigned @@ -132,7 +146,8 @@ public final class TestRunningTaskInfoBuilder { public ActivityManager.RunningTaskInfo build() { final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); - info.taskId = sNextTaskId++; + info.taskId = (mTaskId == INVALID_TASK_ID) ? sNextTaskId++ : mTaskId; + info.effectiveUid = mUid; info.baseIntent = mBaseIntent; info.parentTaskId = mParentTaskId; info.displayId = mDisplayId; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt index b13746814f58..ef99b000d759 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt @@ -52,6 +52,7 @@ import org.mockito.Mockito.mock import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -366,7 +367,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(wct, task.taskId) + immersiveHandler.exitImmersiveIfApplicable(wct, task) assertThat(wct.hasBoundsChange(task.token)).isFalse() } @@ -384,12 +385,12 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct, task.taskId)?.invoke(transition) + immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(transition) assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId - }).isFalse() + }).isTrue() } @Test @@ -405,7 +406,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(wct, task.taskId)?.invoke(transition) + immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(transition) assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY @@ -565,6 +566,24 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { ).isTrue() } + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun exitImmersive_pendingExit_doesNotExitAgain() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + desktopRepository.setTaskInFullImmersiveState( + displayId = task.displayId, + taskId = task.taskId, + immersive = true + ) + immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(Binder()) + + immersiveHandler.moveTaskToNonImmersive(task) + + verify(mockTransitions, never()).startTransition(any(), any(), any()) + } + private fun createTransitionInfo( @TransitionType type: Int = TRANSIT_CHANGE, @TransitionFlags flags: Int = 0, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt index 07de0716200c..81d59d586dd3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt @@ -21,6 +21,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WindowingMode +import android.os.Binder import android.os.Handler import android.os.IBinder import android.testing.AndroidTestingRunner @@ -107,6 +108,8 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { @Test fun startRemoveTransition_startsCloseTransition() { val wct = WindowContainerTransaction() + whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler)) + .thenReturn(Binder()) mixedHandler.startRemoveTransition(wct) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt index 0825b6b0d7be..2a82e6e4f7b8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -16,9 +16,12 @@ package com.android.wm.shell.desktopmode +import android.app.ActivityManager.RunningTaskInfo +import android.graphics.Rect import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker import com.android.dx.mockito.inline.extended.ExtendedMockito.verify import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions @@ -27,6 +30,9 @@ import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.window.flags.Flags import com.android.wm.shell.EventLogTags import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestRunningTaskInfoBuilder +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod @@ -39,9 +45,13 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_M import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason import com.google.common.truth.Truth.assertThat +import org.junit.Before import org.junit.Rule import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever /** * Tests for [DesktopModeEventLogger]. @@ -49,6 +59,8 @@ import org.mockito.kotlin.eq class DesktopModeEventLoggerTest : ShellTestCase() { private val desktopModeEventLogger = DesktopModeEventLogger() + val displayController = mock<DisplayController>() + val displayLayout = mock<DisplayLayout>() @JvmField @Rule(order = 0) @@ -60,6 +72,13 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @Rule(order = 1) val setFlagsRule = SetFlagsRule() + @Before + fun setUp() { + doReturn(displayLayout).whenever(displayController).getDisplayLayout(anyInt()) + doReturn(DISPLAY_WIDTH).whenever(displayLayout).width() + doReturn(DISPLAY_HEIGHT).whenever(displayLayout).height() + } + @Test fun logSessionEnter_logsEnterReasonWithNewSessionId() { desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) @@ -467,7 +486,8 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @Test fun logTaskResizingStarted_noOngoingSession_doesNotLog() { - desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) + desktopModeEventLogger.logTaskResizingStarted(ResizeTrigger.CORNER, + null, createTaskInfo()) verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) @@ -478,13 +498,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() { val sessionId = startDesktopModeSession() - desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) + desktopModeEventLogger.logTaskResizingStarted(ResizeTrigger.CORNER, + null, createTaskInfo(), displayController) verify { FrameworkStatsLog.write( eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ - eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER), /* resizing_stage */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE), /* input_method */ @@ -500,7 +521,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_width */ eq(TASK_SIZE_UPDATE.taskWidth), /* display_area */ - eq(TASK_SIZE_UPDATE.displayArea), + eq(DISPLAY_AREA), ) } verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) @@ -508,7 +529,8 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @Test fun logTaskResizingEnded_noOngoingSession_doesNotLog() { - desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) + desktopModeEventLogger.logTaskResizingEnded(ResizeTrigger.CORNER, + null, createTaskInfo()) verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) @@ -519,13 +541,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() { val sessionId = startDesktopModeSession() - desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) + desktopModeEventLogger.logTaskResizingEnded(ResizeTrigger.CORNER, + null, createTaskInfo(), displayController = displayController) verify { FrameworkStatsLog.write( eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ - eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER), /* resizing_stage */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE), /* input_method */ @@ -541,7 +564,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_width */ eq(TASK_SIZE_UPDATE.taskWidth), /* display_area */ - eq(TASK_SIZE_UPDATE.displayArea), + eq(DISPLAY_AREA), ) } verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) @@ -585,8 +608,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { } } + private fun createTaskInfo(): RunningTaskInfo { + return TestRunningTaskInfoBuilder().setTaskId(TASK_ID) + .setUid(TASK_UID) + .setBounds(Rect(TASK_X, TASK_Y, TASK_WIDTH, TASK_HEIGHT)) + .build() + } + private companion object { - private const val sessionId = 1 private const val TASK_ID = 1 private const val TASK_UID = 1 private const val TASK_X = 0 @@ -594,7 +623,9 @@ class DesktopModeEventLoggerTest : ShellTestCase() { private const val TASK_HEIGHT = 100 private const val TASK_WIDTH = 100 private const val TASK_COUNT = 1 - private const val DISPLAY_AREA = 1000 + private const val DISPLAY_WIDTH = 500 + private const val DISPLAY_HEIGHT = 500 + private const val DISPLAY_AREA = DISPLAY_HEIGHT * DISPLAY_WIDTH private val TASK_UPDATE = TaskUpdate( TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 113990e9d7d2..7c336cdb54f6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -39,6 +39,9 @@ import android.content.res.Configuration.ORIENTATION_PORTRAIT import android.graphics.Point import android.graphics.PointF import android.graphics.Rect +import android.hardware.input.InputManager +import android.hardware.input.InputManager.KeyGestureEventHandler +import android.hardware.input.KeyGestureEvent import android.os.Binder import android.os.Bundle import android.os.Handler @@ -50,6 +53,8 @@ import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY import android.view.DragEvent import android.view.Gravity +import android.view.KeyEvent +import android.view.MotionEvent import android.view.SurfaceControl import android.view.WindowInsets import android.view.WindowManager @@ -70,14 +75,18 @@ import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_R import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER import android.window.WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.ExtendedMockito.never import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER import com.android.internal.jank.InteractionJankMonitor import com.android.window.flags.Flags import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE +import com.android.window.flags.Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS import com.android.window.flags.Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP +import com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT import com.android.wm.shell.MockToken import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer @@ -91,6 +100,7 @@ import com.android.wm.shell.common.LaunchAdjacentController import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask @@ -112,6 +122,7 @@ import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.TestRemoteTransition import com.android.wm.shell.transition.Transitions @@ -205,7 +216,11 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener @Mock private lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter @Mock private lateinit var mockHandler: Handler + @Mock private lateinit var desktopModeEventLogger: DesktopModeEventLogger @Mock lateinit var persistentRepository: DesktopPersistentRepository + @Mock private lateinit var mockInputManager: InputManager + @Mock private lateinit var mockFocusTransitionObserver: FocusTransitionObserver + @Mock lateinit var motionEvent: MotionEvent private lateinit var mockitoSession: StaticMockitoSession private lateinit var controller: DesktopTasksController @@ -214,6 +229,7 @@ class DesktopTasksControllerTest : ShellTestCase() { private lateinit var desktopTasksLimiter: DesktopTasksLimiter private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener private lateinit var testScope: CoroutineScope + private lateinit var keyGestureEventHandler: KeyGestureEventHandler private val shellExecutor = TestShellExecutor() @@ -271,6 +287,11 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.setSplitScreenController(splitScreenController) controller.freeformTaskTransitionStarter = freeformTaskTransitionStarter + doAnswer { + keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler) + null + }.whenever(mockInputManager).registerKeyGestureEventHandler(any()) + shellInit.init() val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java) @@ -278,6 +299,8 @@ class DesktopTasksControllerTest : ShellTestCase() { recentsTransitionStateListener = captor.value controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener + + assumeTrue(ENABLE_SHELL_TRANSITIONS) } private fun createController(): DesktopTasksController { @@ -310,6 +333,9 @@ class DesktopTasksControllerTest : ShellTestCase() { recentTasksController, mockInteractionJankMonitor, mockHandler, + mockInputManager, + mockFocusTransitionObserver, + desktopModeEventLogger, ) } @@ -338,9 +364,17 @@ class DesktopTasksControllerTest : ShellTestCase() { val task1 = setUpFreeformTask() val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) - controller.toggleDesktopTaskSize(task1) - verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task1, + STABLE_BOUNDS.height(), + STABLE_BOUNDS.width(), + displayController + ) assertThat(argumentCaptor.value).isTrue() } @@ -357,9 +391,17 @@ class DesktopTasksControllerTest : ShellTestCase() { val task1 = setUpFreeformTask(bounds = stableBounds, active = true) val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) - controller.toggleDesktopTaskSize(task1) - verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task1, + 0, + 0, + displayController + ) assertThat(argumentCaptor.value).isFalse() } @@ -735,7 +777,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) fun handleRequest_newFreeformTaskLaunch_cascadeApplied() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) setUpLandscapeDisplay() val stableBounds = Rect() displayLayout.getStableBoundsForDesktopMode(stableBounds) @@ -754,7 +795,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) setUpLandscapeDisplay() val stableBounds = Rect() displayLayout.getStableBoundsForDesktopMode(stableBounds) @@ -1467,6 +1507,44 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + @EnableFlags( + FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS, + FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT, + FLAG_USE_KEY_GESTURE_EVENT_HANDLER + ) + fun moveToNextDisplay_withKeyGesture() { + // Set up two display ids + whenever(rootTaskDisplayAreaOrganizer.displayIds) + .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY)) + // Create a mock for the target display area: default display + val defaultDisplayArea = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) + whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)) + .thenReturn(defaultDisplayArea) + // Setup a focused task on secondary display, which is expected to move to default display + val task = setUpFreeformTask(displayId = SECOND_DISPLAY) + task.isFocused = true + whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) + whenever(mockFocusTransitionObserver.hasGlobalFocus(eq(task))).thenReturn(true) + + val event = KeyGestureEvent.Builder() + .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY) + .setDisplayId(SECOND_DISPLAY) + .setKeycodes(intArrayOf(KeyEvent.KEYCODE_D)) + .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON) + .build() + val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) + + assertThat(result).isTrue() + with(getLatestWct(type = TRANSIT_CHANGE)) { + assertThat(hierarchyOps).hasSize(1) + assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder()) + assertThat(hierarchyOps[0].isReparent).isTrue() + assertThat(hierarchyOps[0].newParent).isEqualTo(defaultDisplayArea.token.asBinder()) + assertThat(hierarchyOps[0].toTop).isTrue() + } + } + + @Test fun getTaskWindowingMode() { val fullscreenTask = setUpFullscreenTask() val freeformTask = setUpFreeformTask() @@ -1480,8 +1558,9 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun onDesktopWindowClose_noActiveTasks() { + val task = setUpFreeformTask(active = false) val wct = WindowContainerTransaction() - controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = 1) + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task) // Doesn't modify transaction assertThat(wct.hierarchyOps).isEmpty() } @@ -1490,7 +1569,7 @@ class DesktopTasksControllerTest : ShellTestCase() { fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() { val task = setUpFreeformTask() val wct = WindowContainerTransaction() - controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId) + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task) // Doesn't modify transaction assertThat(wct.hierarchyOps).isEmpty() } @@ -1502,7 +1581,7 @@ class DesktopTasksControllerTest : ShellTestCase() { taskRepository.wallpaperActivityToken = wallpaperToken val wct = WindowContainerTransaction() - controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId) + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task) // Adds remove wallpaper operation wct.assertRemoveAt(index = 0, wallpaperToken) } @@ -1515,7 +1594,7 @@ class DesktopTasksControllerTest : ShellTestCase() { taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId) val wct = WindowContainerTransaction() - controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId) + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task) // Doesn't modify transaction assertThat(wct.hierarchyOps).isEmpty() } @@ -1528,7 +1607,7 @@ class DesktopTasksControllerTest : ShellTestCase() { taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId) val wct = WindowContainerTransaction() - controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId) + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task) // Doesn't modify transaction assertThat(wct.hierarchyOps).isEmpty() } @@ -1541,7 +1620,7 @@ class DesktopTasksControllerTest : ShellTestCase() { taskRepository.wallpaperActivityToken = wallpaperToken val wct = WindowContainerTransaction() - controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId) + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1) // Doesn't modify transaction assertThat(wct.hierarchyOps).isEmpty() } @@ -1555,7 +1634,7 @@ class DesktopTasksControllerTest : ShellTestCase() { taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId) val wct = WindowContainerTransaction() - controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId) + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1) // Adds remove wallpaper operation wct.assertRemoveAt(index = 0, wallpaperToken) } @@ -1569,7 +1648,7 @@ class DesktopTasksControllerTest : ShellTestCase() { taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId) val wct = WindowContainerTransaction() - controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId) + controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1) // Adds remove wallpaper operation wct.assertRemoveAt(index = 0, wallpaperToken) } @@ -1715,8 +1794,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val homeTask = setUpHomeTask() val freeformTask = setUpFreeformTask() markTaskVisible(freeformTask) @@ -1733,8 +1810,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val homeTask = setUpHomeTask() val freeformTask = setUpFreeformTask() markTaskVisible(freeformTask) @@ -1760,8 +1835,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask = setUpFreeformTask() markTaskVisible(freeformTask) val fullscreenTask = createFullscreenTask() @@ -1775,8 +1848,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } freeformTasks.forEach { markTaskVisible(it) } val fullscreenTask = createFullscreenTask() @@ -1791,8 +1862,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } freeformTasks.forEach { markTaskVisible(it) } val fullscreenTask = createFullscreenTask() @@ -1808,8 +1877,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val minimizedTask = setUpFreeformTask() taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId) val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } @@ -1830,7 +1897,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM @@ -1847,7 +1913,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_noTasks_enforceDesktop_fullscreenDisplay_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN @@ -1860,8 +1925,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask = setUpFreeformTask() markTaskHidden(freeformTask) val fullscreenTask = createFullscreenTask() @@ -1870,16 +1933,12 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_noOtherTasks_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val fullscreenTask = createFullscreenTask() assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull() } @Test fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY) createFreeformTask(displayId = SECOND_DISPLAY) @@ -1889,8 +1948,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } freeformTasks.forEach { markTaskVisible(it) } val newFreeformTask = createFreeformTask() @@ -1903,8 +1960,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask = setUpFreeformTask() markTaskHidden(freeformTask) @@ -1919,7 +1974,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_relaunchTask_enforceDesktop_freeformDisplay_noWinModeChange() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM @@ -1934,7 +1988,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_relaunchTask_enforceDesktop_fullscreenDisplay_becomesUndefined() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN @@ -1951,8 +2004,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask1 = setUpFreeformTask() val freeformTask2 = createFreeformTask() @@ -1968,8 +2019,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask1 = setUpFreeformTask() val freeformTask2 = createFreeformTask() @@ -1990,8 +2039,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val task = createFreeformTask() val result = controller.handleRequest(Binder(), createTransition(task)) @@ -2003,8 +2050,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val task = createFreeformTask() val result = controller.handleRequest(Binder(), createTransition(task)) @@ -2019,8 +2064,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY) // Second display task createFreeformTask(displayId = SECOND_DISPLAY) @@ -2035,8 +2078,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY) // Second display task createFreeformTask(displayId = SECOND_DISPLAY) @@ -2053,7 +2094,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false) val freeformTask1 = setUpFreeformTask() @@ -2067,7 +2107,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true) val freeformTask1 = setUpFreeformTask() @@ -2081,7 +2120,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_keyguardLocked_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(keyguardManager.isKeyguardLocked).thenReturn(true) val freeformTask = createFreeformTask(displayId = DEFAULT_DISPLAY) @@ -2092,8 +2130,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_notOpenOrToFrontTransition_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val task = TestRunningTaskInfoBuilder() .setActivityType(ACTIVITY_TYPE_STANDARD) @@ -2106,21 +2142,17 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_noTriggerTask_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull() } @Test fun handleRequest_triggerTaskNotStandard_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build() assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull() } @Test fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val task = TestRunningTaskInfoBuilder() .setActivityType(ACTIVITY_TYPE_STANDARD) @@ -2802,7 +2834,8 @@ class DesktopTasksControllerTest : ShellTestCase() { PointF(200f, -200f), /* inputCoordinate */ Rect(100, -100, 500, 1000), /* currentDragBounds */ Rect(0, 50, 2000, 2000), /* validDragArea */ - Rect() /* dragStartBounds */ ) + Rect() /* dragStartBounds */, + motionEvent) val rectAfterEnd = Rect(100, 50, 500, 1150) verify(transitions) .startTransition( @@ -2837,7 +2870,8 @@ class DesktopTasksControllerTest : ShellTestCase() { PointF(200f, 300f), /* inputCoordinate */ currentDragBounds, /* currentDragBounds */ Rect(0, 50, 2000, 2000) /* validDragArea */, - Rect() /* dragStartBounds */) + Rect() /* dragStartBounds */, + motionEvent) verify(transitions) @@ -3084,10 +3118,19 @@ class DesktopTasksControllerTest : ShellTestCase() { val bounds = Rect(0, 0, 100, 100) val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds) - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + STABLE_BOUNDS.height(), + STABLE_BOUNDS.width(), + displayController + ) } @Test @@ -3106,15 +3149,22 @@ class DesktopTasksControllerTest : ShellTestCase() { STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom ) - controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT) + controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, motionEvent) // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds) assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.SNAP_LEFT_MENU, + motionEvent, + task, + expectedBounds.height(), + expectedBounds.width(), + displayController + ) } @Test fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) // Set up task to already be in snapped-left bounds val bounds = Rect( STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom @@ -3129,7 +3179,7 @@ class DesktopTasksControllerTest : ShellTestCase() { // Attempt to snap left again val currentDragBounds = Rect(bounds).apply { offset(-100, 0) } - controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT) + controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, motionEvent) // Assert that task is NOT updated via WCT verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any()) @@ -3142,6 +3192,14 @@ class DesktopTasksControllerTest : ShellTestCase() { eq(bounds), eq(true) ) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.SNAP_LEFT_MENU, + motionEvent, + task, + bounds.height(), + bounds.width(), + displayController + ) } @Test @@ -3152,12 +3210,22 @@ class DesktopTasksControllerTest : ShellTestCase() { } val preDragBounds = Rect(100, 100, 400, 500) val currentDragBounds = Rect(0, 100, 300, 500) + val expectedBounds = + Rect(STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom) controller.handleSnapResizingTask( - task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds) + task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent + ) val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds) assertThat(findBoundsChange(wct, task)).isEqualTo( - Rect(STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom)) + expectedBounds + ) + verify(desktopModeEventLogger, times(1)).logTaskResizingStarted( + ResizeTrigger.DRAG_LEFT, + motionEvent, + task, + displayController + ) } @Test @@ -3170,7 +3238,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val currentDragBounds = Rect(0, 100, 300, 500) controller.handleSnapResizingTask( - task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds) + task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent) verify(mReturnToDragStartAnimator).start( eq(task.taskId), eq(mockSurface), @@ -3178,6 +3246,13 @@ class DesktopTasksControllerTest : ShellTestCase() { eq(preDragBounds), eq(false) ) + verify(desktopModeEventLogger, never()).logTaskResizingStarted( + any(), + any(), + any(), + any(), + any() + ) } @Test @@ -3196,10 +3271,19 @@ class DesktopTasksControllerTest : ShellTestCase() { // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750) - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + expectedBounds.height(), + expectedBounds.width(), + displayController + ) } @Test @@ -3207,8 +3291,12 @@ class DesktopTasksControllerTest : ShellTestCase() { val bounds = Rect(0, 0, 100, 100) val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds) - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds) + verify(desktopModeEventLogger, never()).logTaskResizingEnded( + any(), any(), any(), any(), + any(), any(), any() + ) } @Test @@ -3217,15 +3305,23 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize) // Maximize - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS) // Restore - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + boundsBeforeMaximize.height(), + boundsBeforeMaximize.width(), + displayController + ) } @Test @@ -3236,16 +3332,24 @@ class DesktopTasksControllerTest : ShellTestCase() { } // Maximize - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS.left, boundsBeforeMaximize.top, STABLE_BOUNDS.right, boundsBeforeMaximize.bottom) // Restore - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + boundsBeforeMaximize.height(), + boundsBeforeMaximize.width(), + displayController + ) } @Test @@ -3256,16 +3360,24 @@ class DesktopTasksControllerTest : ShellTestCase() { } // Maximize - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) task.configuration.windowConfiguration.bounds.set(boundsBeforeMaximize.left, STABLE_BOUNDS.top, boundsBeforeMaximize.right, STABLE_BOUNDS.bottom) // Restore - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + boundsBeforeMaximize.height(), + boundsBeforeMaximize.width(), + displayController + ) } @Test @@ -3274,14 +3386,22 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize) // Maximize - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS) // Restore - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) // Assert last bounds before maximize removed after use assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + boundsBeforeMaximize.height(), + boundsBeforeMaximize.width(), + displayController + ) } @@ -3680,14 +3800,11 @@ class DesktopTasksControllerTest : ShellTestCase() { handlerClass: Class<out TransitionHandler>? = null ): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - if (handlerClass == null) { - verify(transitions).startTransition(eq(type), arg.capture(), isNull()) - } else { - verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass)) - } + + if (handlerClass == null) { + verify(transitions).startTransition(eq(type), arg.capture(), isNull()) } else { - verify(shellTaskOrganizer).applyTransaction(arg.capture()) + verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass)) } return arg.value } @@ -3697,43 +3814,27 @@ class DesktopTasksControllerTest : ShellTestCase() { ): WindowContainerTransaction { val arg: ArgumentCaptor<WindowContainerTransaction> = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce()) + verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce()) .startTransition(capture(arg), eq(currentBounds)) - } else { - verify(shellTaskOrganizer).applyTransaction(capture(arg)) - } return arg.value } private fun getLatestEnterDesktopWct(): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any()) - } else { - verify(shellTaskOrganizer).applyTransaction(arg.capture()) - } + verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any()) return arg.value } private fun getLatestDragToDesktopWct(): WindowContainerTransaction { val arg: ArgumentCaptor<WindowContainerTransaction> = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg)) - } else { - verify(shellTaskOrganizer).applyTransaction(capture(arg)) - } + verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg)) return arg.value } private fun getLatestExitDesktopWct(): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any()) - } else { - verify(shellTaskOrganizer).applyTransaction(arg.capture()) - } + verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any()) return arg.value } @@ -3741,27 +3842,15 @@ class DesktopTasksControllerTest : ShellTestCase() { wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds private fun verifyWCTNotExecuted() { - if (ENABLE_SHELL_TRANSITIONS) { - verify(transitions, never()).startTransition(anyInt(), any(), isNull()) - } else { - verify(shellTaskOrganizer, never()).applyTransaction(any()) - } + verify(transitions, never()).startTransition(anyInt(), any(), isNull()) } private fun verifyExitDesktopWCTNotExecuted() { - if (ENABLE_SHELL_TRANSITIONS) { - verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any()) - } else { - verify(shellTaskOrganizer, never()).applyTransaction(any()) - } + verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any()) } private fun verifyEnterDesktopWCTNotExecuted() { - if (ENABLE_SHELL_TRANSITIONS) { - verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any()) - } else { - verify(shellTaskOrganizer, never()).applyTransaction(any()) - } + verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any()) } private fun createTransition( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt index 0bd3e083671e..79e16fea272d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt @@ -607,7 +607,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { ) ) .thenReturn(token) - handler.startDragToDesktopTransition(task, dragAnimator) + handler.startDragToDesktopTransition(task.taskId, dragAnimator) return token } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt index 9b9703fdf6dc..8495580f42a5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt @@ -115,8 +115,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() { freeformTasksInZOrder = freeformTasksInZOrder) val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID) - assertThat(actualDesktop.tasksByTaskIdMap).hasSize(2) - assertThat(actualDesktop.getZOrderedTasks(0)).isEqualTo(2) + assertThat(actualDesktop?.tasksByTaskIdMap).hasSize(2) + assertThat(actualDesktop?.getZOrderedTasks(0)).isEqualTo(2) } } @@ -138,7 +138,7 @@ class DesktopPersistentRepositoryTest : ShellTestCase() { freeformTasksInZOrder = freeformTasksInZOrder) val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID) - assertThat(actualDesktop.tasksByTaskIdMap[task.taskId]?.desktopTaskState) + assertThat(actualDesktop?.tasksByTaskIdMap?.get(task.taskId)?.desktopTaskState) .isEqualTo(DesktopTaskState.MINIMIZED) } } @@ -161,8 +161,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() { freeformTasksInZOrder = freeformTasksInZOrder) val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID) - assertThat(actualDesktop.tasksByTaskIdMap).isEmpty() - assertThat(actualDesktop.zOrderedTasksList).isEmpty() + assertThat(actualDesktop?.tasksByTaskIdMap).isEmpty() + assertThat(actualDesktop?.zOrderedTasksList).isEmpty() } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index a6e33e5e7c29..a252a9db7095 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -320,7 +320,7 @@ public class StageCoordinatorTests extends ShellTestCase { assertEquals(mStageCoordinator.mLastActiveStage, STAGE_TYPE_MAIN); - mStageCoordinator.onFinishedWakingUp(); + mStageCoordinator.onStartedWakingUp(); verify(mTaskOrganizer).startNewTransition(eq(TRANSIT_SPLIT_DISMISS), notNull()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index 189684dc391a..7144a1e038f9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -114,7 +114,6 @@ public final class StageTaskListenerTests extends ShellTestCase { public void testRootTaskAppeared() { assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId); verify(mCallbacks).onRootTaskAppeared(); - verify(mCallbacks, never()).onStageHasChildrenChanged(mStageTaskListener); verify(mCallbacks, never()).onStageVisibilityChanged(mStageTaskListener); } 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 175fbd2396e3..1839b8a367fe 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 @@ -87,6 +87,8 @@ import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler +import com.android.wm.shell.desktopmode.DesktopModeEventLogger +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopRepository import com.android.wm.shell.desktopmode.DesktopTasksController import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition @@ -194,7 +196,11 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockAppHandleEducationController: AppHandleEducationController @Mock private lateinit var mockFocusTransitionObserver: FocusTransitionObserver @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository + @Mock private lateinit var motionEvent: MotionEvent + @Mock lateinit var displayController: DisplayController + @Mock lateinit var displayLayout: DisplayLayout private lateinit var spyContext: TestableContext + private lateinit var desktopModeEventLogger: DesktopModeEventLogger private val transactionFactory = Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() @@ -224,6 +230,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { shellInit = ShellInit(mockShellExecutor) windowDecorByTaskIdSpy.clear() spyContext.addMockSystemService(InputManager::class.java, mockInputManager) + desktopModeEventLogger = mock<DesktopModeEventLogger>() desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( spyContext, mockShellExecutor, @@ -256,7 +263,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockCaptionHandleRepository, Optional.of(mockActivityOrientationChangeHandler), mockTaskPositionerFactory, - mockFocusTransitionObserver + mockFocusTransitionObserver, + desktopModeEventLogger ) desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController) whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) @@ -299,6 +307,10 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { argumentCaptor<DesktopModeKeyguardChangeListener>() verify(mockShellController).addKeyguardChangeListener(keyguardChangedCaptor.capture()) desktopModeOnKeyguardChangedListener = keyguardChangedCaptor.firstValue + whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout) + whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(STABLE_BOUNDS) + } } @After @@ -612,7 +624,11 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { maxOrRestoreListenerCaptor.value.invoke() - verify(mockDesktopTasksController).toggleDesktopTaskSize(decor.mTaskInfo) + verify(mockDesktopTasksController).toggleDesktopTaskSize( + decor.mTaskInfo, + ResizeTrigger.MAXIMIZE_MENU, + null + ) } @Test @@ -647,7 +663,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { eq(decor.mTaskInfo), taskSurfaceCaptor.capture(), eq(currentBounds), - eq(SnapPosition.LEFT) + eq(SnapPosition.LEFT), + eq(ResizeTrigger.SNAP_LEFT_MENU), + eq(null) ) assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface) } @@ -685,7 +703,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { eq(decor.mTaskInfo), taskSurfaceCaptor.capture(), eq(currentBounds), - eq(SnapPosition.LEFT) + eq(SnapPosition.LEFT), + eq(ResizeTrigger.SNAP_LEFT_MENU), + eq(null) ) assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @@ -704,7 +724,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onLeftSnapClickListenerCaptor.value.invoke() verify(mockDesktopTasksController, never()) - .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT)) + .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT), + eq(ResizeTrigger.MAXIMIZE_BUTTON), + eq(null)) verify(mockToast).show() } @@ -725,7 +747,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { eq(decor.mTaskInfo), taskSurfaceCaptor.capture(), eq(currentBounds), - eq(SnapPosition.RIGHT) + eq(SnapPosition.RIGHT), + eq(ResizeTrigger.SNAP_RIGHT_MENU), + eq(null) ) assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @@ -763,7 +787,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { eq(decor.mTaskInfo), taskSurfaceCaptor.capture(), eq(currentBounds), - eq(SnapPosition.RIGHT) + eq(SnapPosition.RIGHT), + eq(ResizeTrigger.SNAP_RIGHT_MENU), + eq(null) ) assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @@ -782,7 +808,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onRightSnapClickListenerCaptor.value.invoke() verify(mockDesktopTasksController, never()) - .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT)) + .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT), + eq(ResizeTrigger.MAXIMIZE_BUTTON), + eq(null)) verify(mockToast).show() } @@ -1247,7 +1275,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onClickListenerCaptor.value.onClick(view) verify(mockDesktopTasksController) - .toggleDesktopTaskSize(decor.mTaskInfo) + .toggleDesktopTaskSize(decor.mTaskInfo, ResizeTrigger.MAXIMIZE_BUTTON, null) } private fun createOpenTaskDecoration( @@ -1337,7 +1365,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { whenever( mockDesktopModeWindowDecorFactory.create( any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any(), any()) + any(), any(), any(), any(), any(), any(), any(), any()) ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.user).thenReturn(mockUserHandle) @@ -1378,5 +1406,6 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { private const val TAG = "DesktopModeWindowDecorViewModelTests" private val STABLE_INSETS = Rect(0, 100, 0, 0) private val INITIAL_BOUNDS = Rect(0, 0, 100, 100) + private val STABLE_BOUNDS = Rect(0, 0, 1000, 1000) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 320887212f54..0afb6c10b549 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -106,6 +106,7 @@ import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.CaptionState; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; @@ -210,6 +211,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private MultiInstanceHelper mMockMultiInstanceHelper; @Mock private WindowDecorCaptionHandleRepository mMockCaptionHandleRepository; + @Mock + private DesktopModeEventLogger mDesktopModeEventLogger; @Captor private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener; @Captor @@ -1400,7 +1403,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory, - mMockMultiInstanceHelper, mMockCaptionHandleRepository); + mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger); windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener); windowDecor.setExclusionRegionListener(mMockExclusionRegionListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index fb17ae93030d..cb7fadee9822 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -83,6 +83,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.tests.R; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer; @@ -138,6 +139,8 @@ public class WindowDecorationTests extends ShellTestCase { private SurfaceSyncGroup mMockSurfaceSyncGroup; @Mock private SurfaceControl mMockTaskSurface; + @Mock + private DesktopModeEventLogger mDesktopModeEventLogger; private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions = new ArrayList<>(); @@ -1014,7 +1017,7 @@ public class WindowDecorationTests extends ShellTestCase { new MockObjectSupplier<>(mMockSurfaceControlTransactions, () -> mock(SurfaceControl.Transaction.class)), () -> mMockWindowContainerTransaction, () -> mMockTaskSurface, - mMockSurfaceControlViewHostFactory); + mMockSurfaceControlViewHostFactory, mDesktopModeEventLogger); } private class MockObjectSupplier<T> implements Supplier<T> { @@ -1054,11 +1057,12 @@ public class WindowDecorationTests extends ShellTestCase { Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, - SurfaceControlViewHostFactory surfaceControlViewHostFactory) { + SurfaceControlViewHostFactory surfaceControlViewHostFactory, + DesktopModeEventLogger desktopModeEventLogger) { super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, - surfaceControlViewHostFactory); + surfaceControlViewHostFactory, desktopModeEventLogger); } @Override diff --git a/libs/hwui/tests/common/scenes/WindowBlurKawase.cpp b/libs/hwui/tests/common/scenes/WindowBlurKawase.cpp new file mode 100644 index 000000000000..5905b32e7589 --- /dev/null +++ b/libs/hwui/tests/common/scenes/WindowBlurKawase.cpp @@ -0,0 +1,141 @@ +/* + * 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 <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkRefCnt.h> +#include <SkRuntimeEffect.h> +#include <SkSurface.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <math.h> + +#include "SkImageFilters.h" +#include "TestSceneBase.h" +#include "include/gpu/GpuTypes.h" // from Skia +#include "tests/common/BitmapAllocationTestUtils.h" +#include "utils/Color.h" + +class WindowBlurKawase; + +static TestScene::Registrar _WindowBlurKawase(TestScene::Info{ + "windowblurkawase", "Draws window Kawase blur", + TestScene::simpleCreateScene<WindowBlurKawase>}); + +/** + * Simulates the multi-pass Kawase blur algorithm in + * frameworks/native/libs/renderengine/skia/filters/WindowBlurKawaseFilter.cpp + */ +class WindowBlurKawase : public TestScene { +private: + // Keep in sync with + // frameworks/native/libs/renderengine/skia/filters/KawaseBlurFilter.h + static constexpr uint32_t kMaxPasses = 4; + // Keep in sync with frameworks/native/libs/renderengine/skia/filters/BlurFilter.h + static constexpr float kInputScale = 0.25f; + + static constexpr uint32_t kLoopLength = 500; + static constexpr uint32_t kMaxBlurRadius = 300; + sk_sp<SkRuntimeEffect> mBlurEffect; + + sp<RenderNode> card; + sp<RenderNode> contentNode; + +public: + explicit WindowBlurKawase() { + SkString blurString( + "uniform shader child;" + "uniform float in_blurOffset;" + + "half4 main(float2 xy) {" + "half4 c = child.eval(xy);" + "c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));" + "c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));" + "c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));" + "c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));" + "return half4(c.rgb * 0.2, 1.0);" + "}"); + + auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString); + if (!blurEffect) { + LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str()); + } + mBlurEffect = std::move(blurEffect); + } + + void createContent(int width, int height, Canvas& canvas) override { + contentNode = TestUtils::createNode( + 0, 0, width, height, [width, height](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); + Paint paint; + paint.setColor(Color::Red_500); + canvas.drawRect(0, 0, width / 2, height / 2, paint); + paint.setColor(Color::Blue_500); + canvas.drawRect(width / 2, height / 2, width, height, paint); + }); + + card = TestUtils::createNode( + 0, 0, width, height, + [this](RenderProperties& props, Canvas& canvas) { blurFrame(canvas, 0); }); + canvas.drawRenderNode(card.get()); + } + + void doFrame(int frameNr) override { + int curFrame = frameNr % kLoopLength; + float blurRadius = + (sin((float)curFrame / kLoopLength * M_PI * 2) + 1) * 0.5 * kMaxBlurRadius; + TestUtils::recordNode( + *card, [this, blurRadius](Canvas& canvas) { blurFrame(canvas, blurRadius); }); + } + + void blurFrame(Canvas& canvas, float blurRadius) { + if (blurRadius == 0) { + canvas.drawRenderNode(contentNode.get()); + return; + } + + int width = canvas.width(); + int height = canvas.height(); + float tmpRadius = (float)blurRadius / 2.0f; + uint32_t numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius)); + float radiusByPasses = tmpRadius / (float)numberOfPasses; + + SkRuntimeShaderBuilder blurBuilder(mBlurEffect); + + sp<RenderNode> node = contentNode; + for (int i = 0; i < numberOfPasses; i++) { + blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale * (i + 1); + sk_sp<SkImageFilter> blurFilter = + SkImageFilters::RuntimeShader(blurBuilder, radiusByPasses, "child", nullptr); + // Also downsample the image in the first pass. + float canvasScale = i == 0 ? kInputScale : 1; + + // Apply the blur effect as an image filter. + node = TestUtils::createNode( + 0, 0, width * kInputScale, height * kInputScale, + [node, blurFilter, canvasScale](RenderProperties& props, Canvas& canvas) { + props.mutateLayerProperties().setImageFilter(blurFilter.get()); + canvas.scale(canvasScale, canvasScale); + canvas.drawRenderNode(node.get()); + }); + } + + // Finally upsample the image to its original size. + canvas.scale(1 / kInputScale, 1 / kInputScale); + canvas.drawRenderNode(node.get()); + } +}; diff --git a/libs/hwui/tests/common/scenes/WindowBlurSkia.cpp b/libs/hwui/tests/common/scenes/WindowBlurSkia.cpp new file mode 100644 index 000000000000..36e6d8fbb6ed --- /dev/null +++ b/libs/hwui/tests/common/scenes/WindowBlurSkia.cpp @@ -0,0 +1,104 @@ +/* + * 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 <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkRefCnt.h> +#include <SkRuntimeEffect.h> +#include <SkSurface.h> +#include <include/gpu/ganesh/SkSurfaceGanesh.h> +#include <math.h> + +#include "SkImageFilters.h" +#include "TestSceneBase.h" +#include "include/gpu/GpuTypes.h" // from Skia +#include "tests/common/BitmapAllocationTestUtils.h" +#include "utils/Color.h" + +class WindowBlurSkia; + +static TestScene::Registrar _WindowBlurSkia(TestScene::Info{ + "windowblurskia", "Draws window Skia blur", TestScene::simpleCreateScene<WindowBlurSkia>}); + +/** + * Simulates the Skia window blur in + * frameworks/native/libs/renderengine/skia/filters/GaussianBlurFilter.cpp + */ +class WindowBlurSkia : public TestScene { +private: + // Keep in sync with frameworks/native/libs/renderengine/skia/filters/BlurFilter.h + static constexpr float kInputScale = 0.25f; + + static constexpr uint32_t kLoopLength = 500; + static constexpr uint32_t kMaxBlurRadius = 300; + + sp<RenderNode> card; + sp<RenderNode> contentNode; + +public: + void createContent(int width, int height, Canvas& canvas) override { + contentNode = TestUtils::createNode( + 0, 0, width, height, [width, height](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(Color::White, SkBlendMode::kSrcOver); + Paint paint; + paint.setColor(Color::Red_500); + canvas.drawRect(0, 0, width / 2, height / 2, paint); + paint.setColor(Color::Blue_500); + canvas.drawRect(width / 2, height / 2, width, height, paint); + }); + + card = TestUtils::createNode( + 0, 0, width, height, + [this](RenderProperties& props, Canvas& canvas) { blurFrame(canvas, 0); }); + canvas.drawRenderNode(card.get()); + } + + void doFrame(int frameNr) override { + int curFrame = frameNr % kLoopLength; + float blurRadius = + (sin((float)curFrame / kLoopLength * M_PI * 2) + 1) * 0.5 * kMaxBlurRadius; + TestUtils::recordNode( + *card, [this, blurRadius](Canvas& canvas) { blurFrame(canvas, blurRadius); }); + } + + void blurFrame(Canvas& canvas, float blurRadius) { + if (blurRadius == 0) { + canvas.drawRenderNode(contentNode.get()); + return; + } + + int width = canvas.width(); + int height = canvas.height(); + + // Downsample and blur the image with the Skia blur filter. + sp<RenderNode> node = contentNode; + sk_sp<SkImageFilter> blurFilter = + SkImageFilters::Blur(blurRadius, blurRadius, SkTileMode::kClamp, nullptr, nullptr); + node = TestUtils::createNode( + 0, 0, width * kInputScale, height * kInputScale, + [node, blurFilter](RenderProperties& props, Canvas& canvas) { + props.mutateLayerProperties().setImageFilter(blurFilter.get()); + canvas.scale(kInputScale, kInputScale); + canvas.drawRenderNode(node.get()); + }); + + // Upsample the image to its original size. + canvas.scale(1 / kInputScale, 1 / kInputScale); + canvas.drawRenderNode(node.get()); + } +}; diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index 23097f634464..c7a7ed2a885e 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -17,7 +17,6 @@ #include "Color.h" #include <Properties.h> -#include <aidl/android/hardware/graphics/common/Dataspace.h> #include <android/hardware_buffer.h> #include <android/native_window.h> #include <ui/ColorSpace.h> @@ -222,8 +221,7 @@ android_dataspace ColorSpaceToADataSpace(SkColorSpace* colorSpace, SkColorType c if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) { return HAL_DATASPACE_BT2020; } else if (nearlyEqual(fn, SkNamedTransferFn::kSRGB)) { - return static_cast<android_dataspace>( - ::aidl::android::hardware::graphics::common::Dataspace::DISPLAY_BT2020); + return static_cast<android_dataspace>(HAL_DATASPACE_DISPLAY_BT2020); } } diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java index afa0a3271906..65e83b9bf204 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java +++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java @@ -323,10 +323,15 @@ public final class SoundTriggerDetector { int status; try { - status = mSoundTriggerSession.startRecognition(mSoundModel, - mRecognitionCallback, new RecognitionConfig(captureTriggerAudio, - allowMultipleTriggers, null, null, audioCapabilities), - runInBatterySaver); + status = mSoundTriggerSession.startRecognition( + mSoundModel, + mRecognitionCallback, + new RecognitionConfig.Builder() + .setCaptureRequested(captureTriggerAudio) + .setAllowMultipleTriggers(allowMultipleTriggers) + .setAudioCapabilities(audioCapabilities) + .build(), + runInBatterySaver); } catch (RemoteException e) { return false; } diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml index 2e3ee32e2efb..e3f8fbb88a65 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml @@ -20,6 +20,8 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml index 3e0e18488f36..255b2c92e709 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml @@ -20,6 +20,8 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml new file mode 100644 index 000000000000..94c6924a02f2 --- /dev/null +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:importantForAccessibility="no"> + + <com.android.settingslib.widget.MainSwitchBar + android:id="@+id/settingslib_main_switch_bar" + android:visibility="gone" + android:layout_height="wrap_content" + android:layout_width="match_parent" /> + +</FrameLayout> + + diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml index 7c0eaeaca3de..bf34db93298b 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml @@ -18,7 +18,11 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" - android:layout_width="match_parent"> + android:layout_width="match_parent" + android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingRight="?android:attr/listPreferredItemPaddingRight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> <TextView android:id="@+id/switch_text" diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml index fa908a4ed6c8..bef6e352d854 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml @@ -18,10 +18,6 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" - android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingRight="?android:attr/listPreferredItemPaddingRight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:importantForAccessibility="no"> <com.android.settingslib.widget.MainSwitchBar diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index 3394874797e3..83858d9c9c54 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -81,7 +81,11 @@ public class MainSwitchPreference extends TwoStatePreference } private void init(Context context, AttributeSet attrs) { - setLayoutResource(R.layout.settingslib_main_switch_layout); + boolean isExpressive = SettingsThemeHelper.isExpressiveTheme(context); + int resId = isExpressive + ? R.layout.settingslib_expressive_main_switch_layout + : R.layout.settingslib_main_switch_layout; + setLayoutResource(resId); mSwitchChangeListeners.add(this); if (attrs != null) { final TypedArray a = context.obtainStyledAttributes(attrs, diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt index ad996c7c8f86..b64f5dc49b4b 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceTypes.kt @@ -38,3 +38,11 @@ constructor( @StringRes override val title: Int = 0, @StringRes override val summary: Int = 0, ) : TwoStatePreference + +/** A preference that provides a two-state toggleable option that can be used as a main switch. */ +open class MainSwitchPreference +@JvmOverloads +constructor( + override val key: String, + @StringRes override val title: Int = 0, +) : TwoStatePreference
\ No newline at end of file diff --git a/packages/SettingsLib/Preference/Android.bp b/packages/SettingsLib/Preference/Android.bp index bff95ceb137e..fb06be908733 100644 --- a/packages/SettingsLib/Preference/Android.bp +++ b/packages/SettingsLib/Preference/Android.bp @@ -32,6 +32,7 @@ android_library { static_libs: [ "SettingsLibDataStore", "SettingsLibMetadata", + "SettingsLibMainSwitchPreference", "androidx.annotation_annotation", "androidx.preference_preference", "guava", diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt index 4c2e1ba683f6..43f2cb6e2134 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt @@ -16,6 +16,7 @@ package com.android.settingslib.preference +import com.android.settingslib.metadata.MainSwitchPreference import com.android.settingslib.metadata.PreferenceGroup import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.SwitchPreference @@ -36,6 +37,7 @@ object DefaultPreferenceBindingFactory : PreferenceBindingFactory { is SwitchPreference -> SwitchPreferenceBinding.INSTANCE is PreferenceGroup -> PreferenceGroupBinding.INSTANCE is PreferenceScreenCreator -> PreferenceScreenBinding.INSTANCE + is MainSwitchPreference -> MainSwitchPreferenceBinding.INSTANCE else -> DefaultPreferenceBinding } } diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt index ede970e42e72..d40a6f6c55f4 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt @@ -21,11 +21,13 @@ import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat +import androidx.preference.TwoStatePreference import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceScreenMetadata import com.android.settingslib.metadata.PreferenceTitleProvider +import com.android.settingslib.widget.MainSwitchPreference /** Binding of preference group associated with [PreferenceCategory]. */ interface PreferenceScreenBinding : PreferenceBinding { @@ -64,23 +66,37 @@ interface PreferenceGroupBinding : PreferenceBinding { } } -/** A boolean value type preference associated with [SwitchPreferenceCompat]. */ -interface SwitchPreferenceBinding : PreferenceBinding { - - override fun createWidget(context: Context): Preference = SwitchPreferenceCompat(context) +/** A boolean value type preference associated with the abstract [TwoStatePreference]. */ +interface TwoStatePreferenceBinding : PreferenceBinding { override fun bind(preference: Preference, metadata: PreferenceMetadata) { super.bind(preference, metadata) (metadata as? PersistentPreference<*>) ?.storage(preference.context) ?.getValue(metadata.key, Boolean::class.javaObjectType) - ?.let { (preference as SwitchPreferenceCompat).isChecked = it } + ?.let { (preference as TwoStatePreference).isChecked = it } } +} + +/** A boolean value type preference associated with [SwitchPreferenceCompat]. */ +interface SwitchPreferenceBinding : TwoStatePreferenceBinding { + + override fun createWidget(context: Context): Preference = SwitchPreferenceCompat(context) companion object { @JvmStatic val INSTANCE = object : SwitchPreferenceBinding {} } } +/** A boolean value type preference associated with [MainSwitchPreference]. */ +interface MainSwitchPreferenceBinding : TwoStatePreferenceBinding { + + override fun createWidget(context: Context): Preference = MainSwitchPreference(context) + + companion object { + @JvmStatic val INSTANCE = object : MainSwitchPreferenceBinding {} + } +} + /** Default [PreferenceBinding] for [Preference]. */ object DefaultPreferenceBinding : PreferenceBinding diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java index 4f315a2a2486..63661f698f4e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java @@ -74,7 +74,23 @@ public final class InputRouteManager { new AudioDeviceCallback() { @Override public void onAudioDevicesAdded(@NonNull AudioDeviceInfo[] addedDevices) { - applyDefaultSelectedTypeToAllPresets(); + // Activate the last hot plugged valid input device, to match the output device + // behavior. + @AudioDeviceType int deviceTypeToActivate = mSelectedInputDeviceType; + for (AudioDeviceInfo info : addedDevices) { + if (InputMediaDevice.isSupportedInputDevice(info.getType())) { + deviceTypeToActivate = info.getType(); + } + } + + // Only activate if we find a different valid input device. e.g. if none of the + // addedDevices is supported input device, we don't need to activate anything. + if (mSelectedInputDeviceType != deviceTypeToActivate) { + mSelectedInputDeviceType = deviceTypeToActivate; + AudioDeviceAttributes deviceAttributes = + createInputDeviceAttributes(mSelectedInputDeviceType); + setPreferredDeviceForAllPresets(deviceAttributes); + } } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt index d3c345deebad..f5e6caf0d9b9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt @@ -25,6 +25,7 @@ import android.media.AudioManager.OnCommunicationDeviceChangedListener import android.media.IVolumeController import android.provider.Settings import android.util.Log +import android.view.KeyEvent import androidx.concurrent.futures.DirectExecutor import com.android.internal.util.ConcurrentUtils import com.android.settingslib.volume.data.model.VolumeControllerEvent @@ -104,6 +105,8 @@ interface AudioRepository { @AudioDeviceCategory suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int suspend fun notifyVolumeControllerVisible(isVisible: Boolean) + + fun dispatchMediaKeyEvent(event: KeyEvent) } class AudioRepositoryImpl( @@ -265,6 +268,10 @@ class AudioRepositoryImpl( } } + override fun dispatchMediaKeyEvent(event: KeyEvent) { + audioManager.dispatchMediaKeyEvent(event) + } + private fun getMinVolume(stream: AudioStream): Int = try { audioManager.getStreamMinVolume(stream.value) @@ -320,15 +327,9 @@ private class ProducingVolumeController : IVolumeController.Stub() { mutableEvents.tryEmit(VolumeControllerEvent.SetA11yMode(mode)) } - override fun displayCsdWarning( - csdWarning: Int, - displayDurationMs: Int, - ) { + override fun displayCsdWarning(csdWarning: Int, displayDurationMs: Int) { mutableEvents.tryEmit( - VolumeControllerEvent.DisplayCsdWarning( - csdWarning, - displayDurationMs, - ) + VolumeControllerEvent.DisplayCsdWarning(csdWarning, displayDurationMs) ) } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java index 782cee23fb42..d808a25ebc04 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -138,6 +139,18 @@ public class InputRouteManagerTest { /* address= */ ""); } + private AudioDeviceAttributes getUsbHeadsetDeviceAttributes() { + return new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_INPUT, + AudioDeviceInfo.TYPE_USB_HEADSET, + /* address= */ ""); + } + + private AudioDeviceAttributes getHdmiDeviceAttributes() { + return new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_INPUT, AudioDeviceInfo.TYPE_HDMI, /* address= */ ""); + } + private void onPreferredDevicesForCapturePresetChanged(InputRouteManager inputRouteManager) { final List<AudioDeviceAttributes> audioDeviceAttributesList = new ArrayList<AudioDeviceAttributes>(); @@ -303,21 +316,47 @@ public class InputRouteManagerTest { } @Test - public void onAudioDevicesAdded_shouldApplyDefaultSelectedDeviceToAllPresets() { + public void onAudioDevicesAdded_shouldActivateAddedDevice() { final AudioManager audioManager = mock(AudioManager.class); - AudioDeviceAttributes wiredHeadsetDeviceAttributes = getWiredHeadsetDeviceAttributes(); - when(audioManager.getDevicesForAttributes(INPUT_ATTRIBUTES)) - .thenReturn(Collections.singletonList(wiredHeadsetDeviceAttributes)); - InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager); AudioDeviceInfo[] devices = {mockWiredHeadsetInfo()}; inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices); - // Called twice, one after initiation, the other after onAudioDevicesAdded call. - verify(audioManager, atLeast(2)).getDevicesForAttributes(INPUT_ATTRIBUTES); + // The only added wired headset will be activated. for (@MediaRecorder.Source int preset : PRESETS) { - verify(audioManager, atLeast(2)) - .setPreferredDeviceForCapturePreset(preset, wiredHeadsetDeviceAttributes); + verify(audioManager, atLeast(1)) + .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes()); + } + } + + @Test + public void onAudioDevicesAdded_shouldActivateLastAddedDevice() { + final AudioManager audioManager = mock(AudioManager.class); + InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager); + AudioDeviceInfo[] devices = {mockWiredHeadsetInfo(), mockUsbHeadsetInfo()}; + inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices); + + // When adding multiple valid input devices, the last added device (usb headset in this + // case) will be activated. + for (@MediaRecorder.Source int preset : PRESETS) { + verify(audioManager, never()) + .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes()); + verify(audioManager, atLeast(1)) + .setPreferredDeviceForCapturePreset(preset, getUsbHeadsetDeviceAttributes()); + } + } + + @Test + public void onAudioDevicesAdded_doNotActivateInvalidAddedDevice() { + final AudioManager audioManager = mock(AudioManager.class); + InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager); + AudioDeviceInfo[] devices = {mockHdmiInfo()}; + inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices); + + // Do not activate since HDMI is not a valid input device. + for (@MediaRecorder.Source int preset : PRESETS) { + verify(audioManager, never()) + .setPreferredDeviceForCapturePreset(preset, getHdmiDeviceAttributes()); } } diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 65b22758946d..00ae05ceabd4 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -76,6 +76,7 @@ android_test { "truth", "Nene", "Harrier", + "bedstead-enterprise", ], libs: [ "android.test.base.stubs.system", diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java index e86e72712b48..9cce43160b52 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java @@ -20,6 +20,9 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_ENABLED; import static android.provider.Settings.Secure.SYNC_PARENT_SOUNDS; import static android.provider.Settings.System.RINGTONE; +import static com.android.bedstead.enterprise.EnterpriseDeviceStateExtensionsKt.workProfile; +import static com.android.bedstead.multiuser.MultiUserDeviceStateExtensionsKt.secondaryUser; + import static com.google.common.truth.Truth.assertThat; import android.content.pm.PackageManager; @@ -82,7 +85,7 @@ public class SettingsProviderMultiUsersTest { @RequireFeature(PackageManager.FEATURE_MANAGED_USERS) @EnsureHasWorkProfile public void testSettings_workProfile() throws Exception { - UserReference profile = sDeviceState.workProfile(); + UserReference profile = workProfile(sDeviceState); // Settings.Global settings are shared between different users assertSettingsShared(SPACE_GLOBAL, mPrimaryUser.id(), profile.id()); @@ -96,7 +99,7 @@ public class SettingsProviderMultiUsersTest { @RequireRunOnInitialUser @EnsureHasSecondaryUser public void testSettings_secondaryUser() throws Exception { - UserReference secondaryUser = sDeviceState.secondaryUser(); + UserReference secondaryUser = secondaryUser(sDeviceState); // Settings.Global settings are shared between different users assertSettingsShared(SPACE_GLOBAL, mPrimaryUser.id(), secondaryUser.id()); @@ -223,7 +226,7 @@ public class SettingsProviderMultiUsersTest { @RequireRunOnInitialUser @EnsureHasSecondaryUser public void testSettings_stopAndRestartSecondaryUser() throws Exception { - UserReference secondaryUser = sDeviceState.secondaryUser(); + UserReference secondaryUser = secondaryUser(sDeviceState); assertSettingsDifferent(SPACE_SECURE, mPrimaryUser.id(), secondaryUser.id()); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 3c560fdcadb6..5f90b39808f8 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -276,8 +276,6 @@ filegroup { filegroup { name: "SystemUI-tests-broken-robofiles-compile", srcs: [ - "tests/src/**/*DeviceOnlyTest.java", - "tests/src/**/*DeviceOnlyTest.kt", "tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt", "tests/src/**/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt", "tests/src/**/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt", @@ -293,7 +291,6 @@ filegroup { "tests/src/**/systemui/keyguard/ResourceTrimmerTest.kt", "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt", "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt", - "tests/src/**/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt", "tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt", "tests/src/**/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt", "tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt", @@ -303,14 +300,12 @@ filegroup { "tests/src/**/systemui/screenshot/ActionIntentCreatorTest.kt", "tests/src/**/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt", "tests/src/**/systemui/screenshot/TakeScreenshotServiceTest.kt", - "tests/src/**/systemui/statusbar/commandline/CommandRegistryTest.kt", "tests/src/**/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt", "tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt", "tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt", "tests/src/**/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt", "tests/src/**/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt", "tests/src/**/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt", - "tests/src/**/systemui/statusbar/policy/BatteryStateNotifierTest.kt", "tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt", "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt", "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt", @@ -449,27 +444,20 @@ filegroup { "tests/src/**/systemui/media/dialog/MediaSwitchingControllerTest.java", "tests/src/**/systemui/navigationbar/views/NavigationBarTest.java", "tests/src/**/systemui/power/PowerNotificationWarningsTest.java", - "tests/src/**/systemui/power/PowerUITest.java", "tests/src/**/systemui/qs/QSFooterViewControllerTest.java", "tests/src/**/systemui/qs/QSImplTest.java", - "tests/src/**/systemui/qs/QSSecurityFooterTest.java", - "tests/src/**/systemui/qs/tileimpl/QSTileImplTest.java", "tests/src/**/systemui/qs/tiles/QuickAccessWalletTileTest.java", "tests/src/**/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java", - "tests/src/**/systemui/shared/plugins/PluginActionManagerTest.java", - "tests/src/**/systemui/statusbar/CommandQueueTest.java", - "tests/src/**/systemui/statusbar/connectivity/CallbackHandlerTest.java", "tests/src/**/systemui/statusbar/connectivity/NetworkControllerBaseTest.java", + "tests/src/**/systemui/statusbar/connectivity/NetworkControllerDataTest.java", + "tests/src/**/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java", + "tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java", + "tests/src/**/systemui/statusbar/connectivity/NetworkControllerWifiTest.java", "tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java", "tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java", "tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java", "tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java", - "tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java", "tests/src/**/systemui/toast/ToastUITest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerDataTest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerWifiTest.java", ], visibility: ["//visibility:private"], } diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 426b24dfe070..1c29db128a8c 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1148,6 +1148,13 @@ flag { } flag { + name: "media_controls_button_media3_placement" + namespace: "systemui" + description: "Use media3 API for action button placement preferences" + bug: "360196209" +} + +flag { name: "media_controls_drawables_reuse" namespace: "systemui" description: "Re-use created media drawables for media controls" @@ -1552,8 +1559,28 @@ flag { } flag { + name: "hide_ringer_button_in_single_volume_mode" + namespace: "systemui" + description: "When the device is in single volume mode, hide the ringer button because it doesn't work" + bug: "374870615" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "qs_tile_detailed_view" namespace: "systemui" description: "Enables the tile detailed view UI." bug: "374173773" } + +flag { + name: "ensure_enr_views_visibility" + namespace: "systemui" + description: "Ensures public and private visibilities" + bug: "361552380" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 8b0daf6e75b2..476cced6a03d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -159,6 +159,7 @@ import androidx.compose.ui.unit.times import androidx.compose.ui.util.fastAll import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.NoOpUpdate +import androidx.compose.ui.zIndex import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.window.layout.WindowMetricsCalculator import com.android.compose.animation.Easings.Emphasized @@ -796,6 +797,7 @@ private fun BoxScope.CommunalHubLazyGrid( } if (viewModel.isEditMode && dragDropState != null) { + val isItemDragging = dragDropState.draggingItemKey == item.key val outlineAlpha by animateFloatAsState( targetValue = if (selected) 1f else 0f, @@ -812,13 +814,13 @@ private fun BoxScope.CommunalHubLazyGrid( enabled = selected, alpha = { outlineAlpha }, modifier = - Modifier.requiredSize(dpSize).thenIf( - dragDropState.draggingItemKey != item.key - ) { - Modifier.animateItem( - placementSpec = spring(stiffness = Spring.StiffnessMediumLow) - ) - }, + Modifier.requiredSize(dpSize) + .thenIf(!isItemDragging) { + Modifier.animateItem( + placementSpec = spring(stiffness = Spring.StiffnessMediumLow) + ) + } + .thenIf(isItemDragging) { Modifier.zIndex(1f) }, onResize = { resizeInfo -> contentListState.resize(index, resizeInfo) }, minHeightPx = widgetSizeInfo.minHeightPx, maxHeightPx = widgetSizeInfo.maxHeightPx, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 9f99c372e2a8..8469007eddb6 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -710,6 +710,9 @@ internal class NestedScrollHandlerImpl( canStart }, + // We need to maintain scroll priority even if the scene transition can no longer + // consume the scroll gesture to allow us to return to the previous scene. + canStopOnScroll = { _, _ -> false }, canStopOnPreFling = { true }, onStart = { offsetAvailable -> val pointersInfo = pointersInfo() @@ -740,7 +743,12 @@ internal class NestedScrollHandlerImpl( .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene) .invoke() } finally { - dragController = null + // onStop might still be running when a new gesture begins. + // To prevent conflicts, we should only remove the drag controller if it's the + // same one that was active initially. + if (dragController == controller) { + dragController = null + } } }, onCancel = { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt index 6a5b7e103dbb..c790ff03aef1 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt @@ -241,6 +241,10 @@ private fun placeholderContentSize( return targetValueInScene } + fun TransitionState.Transition.otherContent(): ContentKey { + return if (fromContent == content) toContent else fromContent + } + // If the element content was already composed in the other overlay/scene, we use that // target size assuming it doesn't change between scenes. // TODO(b/317026105): Provide a way to give a hint size/content for cases where this is @@ -249,8 +253,10 @@ private fun placeholderContentSize( when (val state = movableElementState(elementKey, transitionStates)) { null -> return IntSize.Zero is TransitionState.Idle -> movableElementContentWhenIdle(layoutImpl, elementKey, state) - is TransitionState.Transition -> - if (state.fromContent == content) state.toContent else state.fromContent + is TransitionState.Transition.ReplaceOverlay -> { + state.otherContent().takeIf { it in element.stateByContent } ?: state.currentScene + } + is TransitionState.Transition -> state.otherContent() } val targetValueInOtherContent = element.stateByContent[otherContent]?.targetSize diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt index 3bd59df16f12..7d29a68e3a8d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt @@ -311,6 +311,22 @@ sealed interface TransitionState { return fromContent == content || toContent == content } + /** + * Return [progress] if [content] is equal to [toContent], `1f - progress` if [content] is + * equal to [fromContent], and throw otherwise. + */ + fun progressTo(content: ContentKey): Float { + return when (content) { + toContent -> progress + fromContent -> 1f - progress + else -> + throw IllegalArgumentException( + "content ($content) should be either toContent ($toContent) or " + + "fromContent ($fromContent)" + ) + } + } + /** Run this transition and return once it is finished. */ abstract suspend fun run() diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt index 636c55799ff2..57d236be40ce 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt @@ -46,6 +46,8 @@ import kotlinx.coroutines.coroutineScope * events in post-scroll mode. * @param canStartPostFling lambda that returns true if the connection can start consuming scroll * events in post-fling mode. + * @param canStopOnScroll lambda that returns true if the connection can stop consuming scroll + * events in scroll mode. * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll * events in pre-fling (i.e. as soon as the user lifts their fingers). * @param onStart lambda that is called when the connection starts consuming scroll events. @@ -64,6 +66,9 @@ class PriorityNestedScrollConnection( private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, private val canStartPostFling: (velocityAvailable: Float) -> Boolean, + private val canStopOnScroll: (available: Float, consumed: Float) -> Boolean = { _, consumed -> + consumed == 0f + }, private val canStopOnPreFling: () -> Boolean, private val onStart: (offsetAvailable: Float) -> Unit, private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float, @@ -166,10 +171,6 @@ class PriorityNestedScrollConnection( } } - private fun shouldStop(consumed: Float): Boolean { - return consumed == 0f - } - private fun start( availableOffset: Float, source: NestedScrollSource, @@ -196,7 +197,7 @@ class PriorityNestedScrollConnection( // Step 2: We have the priority and can consume the scroll events. val consumedByScroll = onScroll(offsetAvailable, source) - if (shouldStop(consumedByScroll)) { + if (canStopOnScroll(offsetAvailable, consumedByScroll)) { // Step 3a: We have lost priority and we no longer need to intercept scroll events. cancel() diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index 339445e3483a..f24d93f0d79d 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -849,6 +849,34 @@ class DraggableHandlerTest { } @Test + fun duringATransition_aNewScrollGesture_shouldTakeControl() = runGestureTest { + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) + // First gesture + nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f)) + assertTransition(currentScene = SceneA) + nestedScroll.preFling(available = Velocity.Zero) + assertTransition(currentScene = SceneA) + + // Second gesture, it starts during onStop() animation + nestedScroll.scroll(downOffset(0.1f)) + assertTransition(currentScene = SceneA) + + // Allows onStop() to complete or cancel + advanceUntilIdle() + + // Second gesture continues + nestedScroll.scroll(downOffset(0.1f)) + assertTransition(currentScene = SceneA) + + // Second gesture ends + nestedScroll.preFling(available = Velocity.Zero) + assertTransition(currentScene = SceneA) + + advanceUntilIdle() + assertIdle(currentScene = SceneA) + } + + @Test fun onPreFling_velocityLowerThanThreshold_remainSameScene() = runGestureTest { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f)) @@ -1295,6 +1323,26 @@ class DraggableHandlerTest { } @Test + fun scrollKeepPriorityEvenIfWeCanNoLongerScrollOnThatDirection() = runGestureTest { + // Overscrolling on scene B does nothing. + layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) } + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) + + // Overscroll is disabled, it will scroll up to 100% + nestedScroll.scroll(available = upOffset(fractionOfScreen = 2f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 1f) + + // We need to maintain scroll priority even if the scene transition can no longer consume + // the scroll gesture. + nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 1f) + + // A scroll gesture in the opposite direction allows us to return to the previous scene. + nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.5f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.5f) + } + + @Test fun overscroll_releaseBetween0And100Percent_up() = runGestureTest { // Make scene B overscrollable. layoutState.transitions = transitions { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt index e57702c045c6..09b59394724d 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt @@ -43,12 +43,17 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.animation.scene.TestOverlays.OverlayA +import com.android.compose.animation.scene.TestOverlays.OverlayB import com.android.compose.animation.scene.TestScenes.SceneA import com.android.compose.animation.scene.TestScenes.SceneB import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.subjects.assertThat import com.android.compose.test.assertSizeIsEqualTo +import com.android.compose.test.setContentAndCreateMainScope +import com.android.compose.test.transition import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.launch import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -341,4 +346,62 @@ class MovableElementTest { rule.onNodeWithTag("bottomEnd").assertPositionInRootIsEqualTo(200.dp, 200.dp) rule.onNodeWithTag("matchParentSize").assertSizeIsEqualTo(200.dp, 200.dp) } + + @Test + fun useCurrentSceneSizeForPlaceholderWhenReplacingOverlay() { + val foo = + MovableElementKey( + "foo", + + // Always compose foo in SceneA. + contentPicker = + object : StaticElementContentPicker { + override val contents: Set<ContentKey> = setOf(SceneA, OverlayB) + + override fun contentDuringTransition( + element: ElementKey, + transition: TransitionState.Transition, + fromContentZIndex: Float, + toContentZIndex: Float, + ): ContentKey { + return SceneA + } + }, + ) + val fooSize = 50.dp + val fooParentInOverlayTag = "fooParentTagInOverlay" + + @Composable + fun SceneScope.Foo(modifier: Modifier = Modifier) { + // Foo wraps its content, so there is no way for STL to know its size in advance. + MovableElement(foo, modifier) { content { Box(Modifier.size(fooSize)) } } + } + + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutState( + initialScene = SceneA, + initialOverlays = setOf(OverlayA), + ) + } + + val scope = + rule.setContentAndCreateMainScope { + SceneTransitionLayout(state) { + scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo() } } + overlay(OverlayA) { /* empty */ } + overlay(OverlayB) { Box(Modifier.testTag(fooParentInOverlayTag)) { Foo() } } + } + } + + // Start an overlay replace transition. + scope.launch { + state.startTransition(transition(from = OverlayA, to = OverlayB, progress = { 0.5f })) + } + + // The parent of foo should have a correct size in OverlayB even if Foo was never composed + // there by using the size information from SceneA. + rule.waitForIdle() + rule.onNodeWithTag(fooParentInOverlayTag).assertSizeIsEqualTo(fooSize) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index f5bb5ba032c2..a2b263b9f9ed 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertThrows import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -768,4 +769,12 @@ class SceneTransitionLayoutStateTest { assertThat(state.transitionState).isIdle() assertThat(state.transitionState).hasCurrentScene(SceneC) } + + @Test + fun transition_progressTo() { + val transition = transition(from = SceneA, to = SceneB, progress = { 0.2f }) + assertThat(transition.progressTo(SceneB)).isEqualTo(0.2f) + assertThat(transition.progressTo(SceneA)).isEqualTo(1f - 0.2f) + assertThrows(IllegalArgumentException::class.java) { transition.progressTo(SceneC) } + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 3001505d0e02..2bc9b3826548 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -938,6 +938,71 @@ class SwipeToSceneTest { } @Test + fun scrollKeepPriorityEvenIfWeCanNoLongerScrollOnThatDirection() { + val swipeDistance = 100.dp + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutState( + SceneA, + transitions { + from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) } + from(SceneB, to = SceneC) { distance = FixedDistance(swipeDistance) } + overscrollDisabled(SceneB, Orientation.Vertical) + }, + ) + } + val layoutSize = 200.dp + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(state, Modifier.size(layoutSize)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB, Swipe.Right to SceneC)) { + Box( + Modifier.fillMaxSize() + // A scrollable that does not consume the scroll gesture + .scrollable(rememberScrollableState { 0f }, Orientation.Vertical) + ) + } + scene(SceneB, userActions = mapOf(Swipe.Right to SceneC)) { + Box(Modifier.element(TestElements.Foo).fillMaxSize()) + } + scene(SceneC) { Box(Modifier.fillMaxSize()) } + } + } + + fun assertTransition(from: SceneKey, to: SceneKey, progress: Float) { + val transition = assertThat(state.transitionState).isSceneTransition() + assertThat(transition).hasFromScene(from) + assertThat(transition).hasToScene(to) + assertThat(transition.progress).isEqualTo(progress) + } + + // Vertical scroll 100% + rule.onRoot().performTouchInput { + val middle = (layoutSize / 2).toPx() + down(Offset(middle, middle)) + moveBy(Offset(0f, y = touchSlop + swipeDistance.toPx()), delayMillis = 1_000) + } + assertTransition(from = SceneA, to = SceneB, progress = 1f) + + // Continue vertical scroll, should be ignored (overscrollDisabled) + rule.onRoot().performTouchInput { moveBy(Offset(0f, y = touchSlop), delayMillis = 1_000) } + assertTransition(from = SceneA, to = SceneB, progress = 1f) + + // Horizontal scroll, should be ignored + rule.onRoot().performTouchInput { + moveBy(Offset(x = touchSlop + swipeDistance.toPx(), 0f), delayMillis = 1_000) + } + assertTransition(from = SceneA, to = SceneB, progress = 1f) + + // Vertical scroll, in the opposite direction + rule.onRoot().performTouchInput { + moveBy(Offset(0f, -swipeDistance.toPx()), delayMillis = 1_000) + } + assertTransition(from = SceneA, to = SceneB, progress = 0f) + } + + @Test fun sceneWithoutSwipesDoesNotConsumeGestures() { val buttonTag = "button" diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt new file mode 100644 index 000000000000..c342f488212a --- /dev/null +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestReplaceOverlayTransition.kt @@ -0,0 +1,109 @@ +/* + * 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.test + +import androidx.compose.foundation.gestures.Orientation +import com.android.compose.animation.scene.ContentKey +import com.android.compose.animation.scene.OverlayKey +import com.android.compose.animation.scene.SceneTransitionLayoutImpl +import com.android.compose.animation.scene.content.state.TransitionState +import com.android.compose.animation.scene.content.state.TransitionState.Transition +import kotlinx.coroutines.CompletableDeferred + +/** A [Transition.ShowOrHideOverlay] for tests that will be finished once [finish] is called. */ +abstract class TestReplaceOverlayTransition( + fromOverlay: OverlayKey, + toOverlay: OverlayKey, + replacedTransition: Transition?, +) : + Transition.ReplaceOverlay( + fromOverlay = fromOverlay, + toOverlay = toOverlay, + replacedTransition = replacedTransition, + ) { + private val finishCompletable = CompletableDeferred<Unit>() + + override suspend fun run() { + finishCompletable.await() + } + + /** Finish this transition. */ + fun finish() { + finishCompletable.complete(Unit) + } +} + +/** A utility to easily create a [TestReplaceOverlayTransition] in tests. */ +fun transition( + from: OverlayKey, + to: OverlayKey, + effectivelyShownOverlay: () -> OverlayKey = { to }, + progress: () -> Float = { 0f }, + progressVelocity: () -> Float = { 0f }, + previewProgress: () -> Float = { 0f }, + previewProgressVelocity: () -> Float = { 0f }, + isInPreviewStage: () -> Boolean = { false }, + interruptionProgress: () -> Float = { 0f }, + isInitiatedByUserInput: Boolean = false, + isUserInputOngoing: Boolean = false, + isUpOrLeft: Boolean = false, + bouncingContent: ContentKey? = null, + orientation: Orientation = Orientation.Horizontal, + onFreezeAndAnimate: ((TestReplaceOverlayTransition) -> Unit)? = null, + replacedTransition: Transition? = null, +): TestReplaceOverlayTransition { + return object : + TestReplaceOverlayTransition(from, to, replacedTransition), + TransitionState.HasOverscrollProperties { + override val effectivelyShownOverlay: OverlayKey + get() = effectivelyShownOverlay() + + override val progress: Float + get() = progress() + + override val progressVelocity: Float + get() = progressVelocity() + + override val previewProgress: Float + get() = previewProgress() + + override val previewProgressVelocity: Float + get() = previewProgressVelocity() + + override val isInPreviewStage: Boolean + get() = isInPreviewStage() + + override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput + override val isUserInputOngoing: Boolean = isUserInputOngoing + override val isUpOrLeft: Boolean = isUpOrLeft + override val bouncingContent: ContentKey? = bouncingContent + override val orientation: Orientation = orientation + override val absoluteDistance = 0f + + override fun freezeAndAnimateToCurrentState() { + if (onFreezeAndAnimate != null) { + onFreezeAndAnimate(this) + } else { + finish() + } + } + + override fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float { + return interruptionProgress() + } + } +} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index 900971bc3927..2a858238c156 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -39,7 +39,7 @@ class DefaultClockProvider( val resources: Resources, private val hasStepClockAnimation: Boolean = false, private val migratedClocks: Boolean = false, - private val clockReactiveVariants: Boolean = false, + private val isClockReactiveVariantsEnabled: Boolean = false, ) : ClockProvider { private var messageBuffers: ClockMessageBuffers? = null @@ -54,7 +54,7 @@ class DefaultClockProvider( throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG") } - return if (clockReactiveVariants) { + return if (isClockReactiveVariantsEnabled) { val buffer = messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO) val assets = AssetLoader(ctx, ctx, "clocks/", buffer) @@ -84,7 +84,7 @@ class DefaultClockProvider( // TODO(b/352049256): Update placeholder to actual resource resources.getDrawable(R.drawable.clock_default_thumbnail, null), isReactiveToTone = true, - isReactiveToTouch = clockReactiveVariants, + isReactiveToTouch = isClockReactiveVariantsEnabled, axes = listOf(), // TODO: Ater some picker definition ) } @@ -103,7 +103,6 @@ class DefaultClockProvider( timespec = DigitalTimespec.FIRST_DIGIT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", lineHeight = 147.25f, fontVariation = "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100", @@ -112,7 +111,6 @@ class DefaultClockProvider( FontTextStyle( fontVariation = "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100", - fontFamily = "google_sans_flex.ttf", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", renderType = RenderType.CHANGE_WEIGHT, @@ -131,7 +129,6 @@ class DefaultClockProvider( timespec = DigitalTimespec.SECOND_DIGIT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", lineHeight = 147.25f, fontVariation = "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100", @@ -140,7 +137,6 @@ class DefaultClockProvider( FontTextStyle( fontVariation = "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100", - fontFamily = "google_sans_flex.ttf", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", renderType = RenderType.CHANGE_WEIGHT, @@ -159,7 +155,6 @@ class DefaultClockProvider( timespec = DigitalTimespec.FIRST_DIGIT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", lineHeight = 147.25f, fontVariation = "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100", @@ -168,7 +163,6 @@ class DefaultClockProvider( FontTextStyle( fontVariation = "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100", - fontFamily = "google_sans_flex.ttf", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", renderType = RenderType.CHANGE_WEIGHT, @@ -187,7 +181,6 @@ class DefaultClockProvider( timespec = DigitalTimespec.SECOND_DIGIT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", lineHeight = 147.25f, fontVariation = "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100", @@ -196,7 +189,6 @@ class DefaultClockProvider( FontTextStyle( fontVariation = "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100", - fontFamily = "google_sans_flex.ttf", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", renderType = RenderType.CHANGE_WEIGHT, @@ -221,13 +213,11 @@ class DefaultClockProvider( timespec = DigitalTimespec.TIME_FULL_FORMAT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", fontVariation = "'wght' 600, 'wdth' 100, 'opsz' 144, 'ROND' 100", fontSizeScale = 0.98f, ), aodStyle = FontTextStyle( - fontFamily = "google_sans_flex.ttf", fontVariation = "'wght' 133, 'wdth' 43, 'opsz' 144, 'ROND' 100", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index af3ddfca14b6..29ee87466f1a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -17,6 +17,9 @@ package com.android.systemui.bouncer.ui.viewmodel import android.content.pm.UserInfo +import android.platform.test.annotations.EnableFlags +import android.view.KeyEvent +import androidx.compose.ui.input.key.KeyEventType import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.SceneKey @@ -27,6 +30,7 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.bouncer.domain.interactor.bouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.inputmethod.data.model.InputMethodModel import com.android.systemui.inputmethod.data.repository.fakeInputMethodRepository import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor @@ -67,11 +71,12 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { private val inputMethodInteractor by lazy { kosmos.inputMethodInteractor } private val isInputEnabled = MutableStateFlow(true) - private val underTest = + private val underTest by lazy { kosmos.passwordBouncerViewModelFactory.create( isInputEnabled = isInputEnabled, onIntentionalUserInput = {}, ) + } @Before fun setUp() { @@ -345,6 +350,37 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(textInputFocusRequested).isFalse() } + @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER) + @Test + fun consumeConfirmKeyEvents_toPreventItFromPropagating() = + testScope.runTest { verifyConfirmKeyEventsBehavior(keyUpEventConsumed = true) } + + @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER) + @EnableSceneContainer + @Test + fun noops_whenSceneContainerIsAlsoEnabled() = + testScope.runTest { verifyConfirmKeyEventsBehavior(keyUpEventConsumed = false) } + + private fun verifyConfirmKeyEventsBehavior(keyUpEventConsumed: Boolean) { + assertThat(underTest.onKeyEvent(KeyEventType.KeyDown, KeyEvent.KEYCODE_DPAD_CENTER)) + .isFalse() + assertThat(underTest.onKeyEvent(KeyEventType.KeyUp, KeyEvent.KEYCODE_DPAD_CENTER)) + .isEqualTo(keyUpEventConsumed) + + assertThat(underTest.onKeyEvent(KeyEventType.KeyDown, KeyEvent.KEYCODE_ENTER)).isFalse() + assertThat(underTest.onKeyEvent(KeyEventType.KeyUp, KeyEvent.KEYCODE_ENTER)) + .isEqualTo(keyUpEventConsumed) + + assertThat(underTest.onKeyEvent(KeyEventType.KeyDown, KeyEvent.KEYCODE_NUMPAD_ENTER)) + .isFalse() + assertThat(underTest.onKeyEvent(KeyEventType.KeyUp, KeyEvent.KEYCODE_NUMPAD_ENTER)) + .isEqualTo(keyUpEventConsumed) + + // space is ignored. + assertThat(underTest.onKeyEvent(KeyEventType.KeyUp, KeyEvent.KEYCODE_SPACE)).isFalse() + assertThat(underTest.onKeyEvent(KeyEventType.KeyDown, KeyEvent.KEYCODE_SPACE)).isFalse() + } + private fun TestScope.switchToScene(toScene: SceneKey) { val currentScene by collectLastValue(sceneInteractor.currentScene) val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt index 855b6d0b95d7..587d3d98fbec 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SeekbarHapticPluginTest.kt @@ -20,6 +20,7 @@ import android.widget.SeekBar import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.haptics.msdl.msdlPlayer import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.VibratorHelper @@ -141,11 +142,7 @@ class SeekbarHapticPluginTest : SysuiTestCase() { } private fun createPlugin() { - plugin = - SeekbarHapticPlugin( - vibratorHelper, - kosmos.fakeSystemClock, - ) + plugin = SeekbarHapticPlugin(vibratorHelper, kosmos.msdlPlayer, kosmos.fakeSystemClock) } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt index 28f88fe1d84e..3467382df4da 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt @@ -16,16 +16,26 @@ package com.android.systemui.haptics.slider +import android.os.VibrationAttributes import android.os.VibrationEffect +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags 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.haptics.fakeVibratorHelper +import com.android.systemui.haptics.msdl.fakeMSDLPlayer +import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.util.time.fakeSystemClock +import com.google.android.msdl.data.model.MSDLToken +import com.google.android.msdl.domain.InteractionProperties +import com.google.common.truth.Truth.assertThat import kotlin.math.max import kotlin.test.assertEquals import kotlin.test.assertTrue +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -35,6 +45,7 @@ import org.junit.runner.RunWith class SliderHapticFeedbackProviderTest : SysuiTestCase() { private val kosmos = testKosmos() + private val testScope = kosmos.testScope private val config = SliderHapticFeedbackConfig() @@ -44,7 +55,14 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { private val dragTextureThresholdMillis = lowTickDuration * config.numberOfLowTicks + config.deltaMillisForDragInterval private val vibratorHelper = kosmos.fakeVibratorHelper + private val msdlPlayer = kosmos.fakeMSDLPlayer private lateinit var sliderHapticFeedbackProvider: SliderHapticFeedbackProvider + private val pipeliningAttributes = + VibrationAttributes.Builder() + .setUsage(VibrationAttributes.USAGE_TOUCH) + .setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT) + .build() + private lateinit var dynamicProperties: InteractionProperties.DynamicVibrationScale @Before fun setup() { @@ -54,13 +72,20 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { sliderHapticFeedbackProvider = SliderHapticFeedbackProvider( vibratorHelper, + msdlPlayer, dragVelocityProvider, config, kosmos.fakeSystemClock, ) + dynamicProperties = + InteractionProperties.DynamicVibrationScale( + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), + pipeliningAttributes, + ) } @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtLowerBookend_playsClick() = with(kosmos) { val vibration = @@ -77,6 +102,18 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtLowerBookend_playsDragThresholdLimitToken() = + testScope.runTest { + sliderHapticFeedbackProvider.onLowerBookend() + + assertThat(msdlPlayer.latestTokenPlayed) + .isEqualTo(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT) + assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(dynamicProperties) + } + + @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtLowerBookend_twoTimes_playsClickOnlyOnce() = with(kosmos) { val vibration = @@ -94,6 +131,20 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtLowerBookend_twoTimes_playsDragThresholdLimitTokenOnlyOnce() = + testScope.runTest { + sliderHapticFeedbackProvider.onLowerBookend() + sliderHapticFeedbackProvider.onLowerBookend() + + assertThat(msdlPlayer.latestTokenPlayed) + .isEqualTo(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT) + assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(dynamicProperties) + assertThat(msdlPlayer.getHistory().size).isEqualTo(1) + } + + @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtUpperBookend_playsClick() = with(kosmos) { val vibration = @@ -110,6 +161,18 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtUpperBookend_playsDragThresholdLimitToken() = + testScope.runTest { + sliderHapticFeedbackProvider.onUpperBookend() + + assertThat(msdlPlayer.latestTokenPlayed) + .isEqualTo(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT) + assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(dynamicProperties) + } + + @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtUpperBookend_twoTimes_playsClickOnlyOnce() = with(kosmos) { val vibration = @@ -127,6 +190,20 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtUpperBookend_twoTimes_playsDragThresholdLimitTokenOnlyOnce() = + testScope.runTest { + sliderHapticFeedbackProvider.onUpperBookend() + sliderHapticFeedbackProvider.onUpperBookend() + + assertThat(msdlPlayer.latestTokenPlayed) + .isEqualTo(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT) + assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(dynamicProperties) + assertThat(msdlPlayer.getHistory().size).isEqualTo(1) + } + + @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtProgress_onQuickSuccession_playsLowTicksOnce() = with(kosmos) { // GIVEN max velocity and slider progress @@ -150,6 +227,31 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtProgress_onQuickSuccession_playsContinuousDragTokenOnce() = + with(kosmos) { + // GIVEN max velocity and slider progress + val progress = 1f + val expectedScale = + sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress) + val expectedProperties = + InteractionProperties.DynamicVibrationScale(expectedScale, pipeliningAttributes) + + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) + + // WHEN two calls to play occur immediately + sliderHapticFeedbackProvider.onProgress(progress) + sliderHapticFeedbackProvider.onProgress(progress) + + // THEN the correct token plays once + assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.DRAG_INDICATOR_CONTINUOUS) + assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(expectedProperties) + assertThat(msdlPlayer.getHistory().size).isEqualTo(1) + } + + @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtProgress_beforeNextDragThreshold_playsLowTicksOnce() = with(kosmos) { // GIVEN max velocity and a slider progress at half progress @@ -175,6 +277,41 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtProgress_beforeNextDragThreshold_playsContinousDragTokenOnce() = + with(kosmos) { + // GIVEN max velocity and a slider progress at half progress + val firstProgress = 0.5f + + // Given a second slider progress event smaller than the progress threshold + val secondProgress = + firstProgress + max(0f, config.deltaProgressForDragThreshold - 0.01f) + + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) + + // WHEN two calls to play occur with the required threshold separation (time and + // progress) + sliderHapticFeedbackProvider.onProgress(firstProgress) + fakeSystemClock.advanceTime(dragTextureThresholdMillis.toLong()) + sliderHapticFeedbackProvider.onProgress(secondProgress) + + // THEN Only the first event plays the expected token and propertiesv + val expectedProperties = + InteractionProperties.DynamicVibrationScale( + sliderHapticFeedbackProvider.scaleOnDragTexture( + config.maxVelocityToScale, + firstProgress, + ), + pipeliningAttributes, + ) + assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.DRAG_INDICATOR_CONTINUOUS) + assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(expectedProperties) + assertThat(msdlPlayer.getHistory().size).isEqualTo(1) + } + + @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() = with(kosmos) { // GIVEN max velocity and a slider progress at half progress @@ -200,6 +337,51 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtProgress_afterNextDragThreshold_playsContinuousDragTokenTwice() = + with(kosmos) { + // GIVEN max velocity and a slider progress at half progress + val firstProgress = 0.5f + + // Given a second slider progress event beyond progress threshold + val secondProgress = firstProgress + config.deltaProgressForDragThreshold + 0.01f + + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) + + // WHEN two calls to play occur with the required threshold separation (time and + // progress) + sliderHapticFeedbackProvider.onProgress(firstProgress) + fakeSystemClock.advanceTime(dragTextureThresholdMillis.toLong()) + sliderHapticFeedbackProvider.onProgress(secondProgress) + + // THEN the correct token plays twice with the correct properties + val firstProperties = + InteractionProperties.DynamicVibrationScale( + sliderHapticFeedbackProvider.scaleOnDragTexture( + config.maxVelocityToScale, + firstProgress, + ), + pipeliningAttributes, + ) + val secondProperties = + InteractionProperties.DynamicVibrationScale( + sliderHapticFeedbackProvider.scaleOnDragTexture( + config.maxVelocityToScale, + secondProgress, + ), + pipeliningAttributes, + ) + + assertThat(msdlPlayer.getHistory().size).isEqualTo(2) + assertThat(msdlPlayer.tokensPlayed[0]).isEqualTo(MSDLToken.DRAG_INDICATOR_CONTINUOUS) + assertThat(msdlPlayer.propertiesPlayed[0]).isEqualTo(firstProperties) + assertThat(msdlPlayer.tokensPlayed[1]).isEqualTo(MSDLToken.DRAG_INDICATOR_CONTINUOUS) + assertThat(msdlPlayer.propertiesPlayed[1]).isEqualTo(secondProperties) + } + + @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTwice() = with(kosmos) { // GIVEN max velocity and slider progress @@ -233,6 +415,36 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTokensTwice() = + with(kosmos) { + // GIVEN max velocity and slider progress + val progress = 1f + val expectedProperties = + InteractionProperties.DynamicVibrationScale( + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), + pipeliningAttributes, + ) + + // GIVEN a vibration at the lower bookend followed by a request to vibrate at progress + sliderHapticFeedbackProvider.onLowerBookend() + sliderHapticFeedbackProvider.onProgress(progress) + + // WHEN a vibration is to trigger again at the lower bookend + sliderHapticFeedbackProvider.onLowerBookend() + + // THEN there are two bookend token vibrations + assertThat(msdlPlayer.getHistory().size).isEqualTo(2) + assertThat(msdlPlayer.tokensPlayed[0]) + .isEqualTo(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT) + assertThat(msdlPlayer.propertiesPlayed[0]).isEqualTo(expectedProperties) + assertThat(msdlPlayer.tokensPlayed[1]) + .isEqualTo(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT) + assertThat(msdlPlayer.propertiesPlayed[1]).isEqualTo(expectedProperties) + } + + @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTwice() = with(kosmos) { // GIVEN max velocity and slider progress @@ -265,6 +477,36 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { ) } + @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTokensTwice() = + with(kosmos) { + // GIVEN max velocity and slider progress + val progress = 1f + val expectedProperties = + InteractionProperties.DynamicVibrationScale( + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), + pipeliningAttributes, + ) + + // GIVEN a vibration at the upper bookend followed by a request to vibrate at progress + sliderHapticFeedbackProvider.onUpperBookend() + sliderHapticFeedbackProvider.onProgress(progress) + + // WHEN a vibration is to trigger again at the upper bookend + sliderHapticFeedbackProvider.onUpperBookend() + + // THEN there are two bookend vibrations + assertThat(msdlPlayer.getHistory().size).isEqualTo(2) + assertThat(msdlPlayer.tokensPlayed[0]) + .isEqualTo(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT) + assertThat(msdlPlayer.propertiesPlayed[0]).isEqualTo(expectedProperties) + assertThat(msdlPlayer.tokensPlayed[1]) + .isEqualTo(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT) + assertThat(msdlPlayer.propertiesPlayed[1]).isEqualTo(expectedProperties) + } + + @Test fun dragTextureLastProgress_afterDragTextureHaptics_keepsLastDragTextureProgress() = with(kosmos) { // GIVEN max velocity and a slider progress at half progress diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 93754fd7e778..77f19795eaf7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -314,16 +314,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { } @Test - fun isActiveDreamLockscreenHosted() = - testScope.runTest { - underTest.setIsActiveDreamLockscreenHosted(true) - assertThat(underTest.isActiveDreamLockscreenHosted.value).isEqualTo(true) - - underTest.setIsActiveDreamLockscreenHosted(false) - assertThat(underTest.isActiveDreamLockscreenHosted.value).isEqualTo(false) - } - - @Test fun isUdfpsSupported() = testScope.runTest { whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true) @@ -336,26 +326,14 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { @Test fun isKeyguardGoingAway() = testScope.runTest { - whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false) - var latest: Boolean? = null - val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this) - runCurrent() - assertThat(latest).isFalse() - - val captor = argumentCaptor<KeyguardStateController.Callback>() - verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture()) + val isGoingAway by collectLastValue(underTest.isKeyguardGoingAway) + assertThat(isGoingAway).isFalse() - whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true) - captor.value.onKeyguardGoingAwayChanged() - runCurrent() - assertThat(latest).isTrue() - - whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false) - captor.value.onKeyguardGoingAwayChanged() - runCurrent() - assertThat(latest).isFalse() + underTest.isKeyguardGoingAway.value = true + assertThat(isGoingAway).isTrue() - job.cancel() + underTest.isKeyguardGoingAway.value = false + assertThat(isGoingAway).isFalse() } @Test @@ -423,17 +401,17 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { runCurrent() listener.onDozeTransition( DozeMachine.State.DOZE_REQUEST_PULSE, - DozeMachine.State.DOZE_PULSING + DozeMachine.State.DOZE_PULSING, ) runCurrent() listener.onDozeTransition( DozeMachine.State.DOZE_SUSPEND_TRIGGERS, - DozeMachine.State.DOZE_PULSE_DONE + DozeMachine.State.DOZE_PULSE_DONE, ) runCurrent() listener.onDozeTransition( DozeMachine.State.DOZE_AOD_PAUSING, - DozeMachine.State.DOZE_AOD_PAUSED + DozeMachine.State.DOZE_AOD_PAUSED, ) runCurrent() @@ -443,22 +421,22 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { // Initial value will be UNINITIALIZED DozeTransitionModel( DozeStateModel.UNINITIALIZED, - DozeStateModel.UNINITIALIZED + DozeStateModel.UNINITIALIZED, ), DozeTransitionModel(DozeStateModel.INITIALIZED, DozeStateModel.DOZE), DozeTransitionModel(DozeStateModel.DOZE, DozeStateModel.DOZE_AOD), DozeTransitionModel(DozeStateModel.DOZE_AOD_DOCKED, DozeStateModel.FINISH), DozeTransitionModel( DozeStateModel.DOZE_REQUEST_PULSE, - DozeStateModel.DOZE_PULSING + DozeStateModel.DOZE_PULSING, ), DozeTransitionModel( DozeStateModel.DOZE_SUSPEND_TRIGGERS, - DozeStateModel.DOZE_PULSE_DONE + DozeStateModel.DOZE_PULSE_DONE, ), DozeTransitionModel( DozeStateModel.DOZE_AOD_PAUSING, - DozeStateModel.DOZE_AOD_PAUSED + DozeStateModel.DOZE_AOD_PAUSED, ), ) ) @@ -510,12 +488,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { // consuming it should handle that properly. assertThat(values).isEqualTo(listOf(null)) - listOf( - Point(500, 500), - Point(0, 0), - null, - Point(250, 250), - ) + listOf(Point(500, 500), Point(0, 0), null, Point(250, 250)) .onEach { facePropertyRepository.setSensorLocation(it) runCurrent() @@ -551,7 +524,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { .onEach { biometricSourceType -> underTest.setBiometricUnlockState( BiometricUnlockMode.NONE, - BiometricUnlockSource.Companion.fromBiometricSourceType(biometricSourceType) + BiometricUnlockSource.Companion.fromBiometricSourceType(biometricSourceType), ) runCurrent() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt index 3b6e5d09a37c..df44425d4b49 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt @@ -90,6 +90,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT private lateinit var powerInteractor: PowerInteractor private lateinit var transitionRepository: FakeKeyguardTransitionRepository + private lateinit var keyguardInteractor: KeyguardInteractor companion object { @JvmStatic @@ -106,6 +107,7 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT @Before fun setup() { powerInteractor = kosmos.powerInteractor + keyguardInteractor = kosmos.keyguardInteractor transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy underTest = kosmos.fromDozingTransitionInteractor @@ -137,6 +139,20 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT } @Test + @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testTransitionToGone_onWakeup_whenGoingAway() = + testScope.runTest { + keyguardInteractor.setIsKeyguardGoingAway(true) + runCurrent() + + powerInteractor.setAwakeForTest() + advanceTimeBy(60L) + + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GONE) + } + + @Test @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR) fun testTransitionToLockscreen_onWake_canDream_glanceableHubAvailable() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt index 48621047016b..e60d971c7289 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt @@ -173,17 +173,6 @@ class KeyguardClockInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer - fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_isActiveDreamLockscreenHosted_true() = - testScope.runTest { - val value by collectLastValue(underTest.clockShouldBeCentered) - kosmos.shadeRepository.setShadeLayoutWide(true) - kosmos.activeNotificationListRepository.setActiveNotifs(1) - kosmos.keyguardRepository.setIsActiveDreamLockscreenHosted(true) - assertThat(value).isTrue() - } - - @Test - @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_hasPulsingNotifications_false() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt index 945e44afa455..fbdab7d40c9b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.shade.ShadeController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -53,6 +54,7 @@ import org.mockito.kotlin.isNull @RunWith(AndroidJUnit4::class) class KeyguardKeyEventInteractorTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + private val kosmos = testKosmos() private val actionDownVolumeDownKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN) @@ -85,6 +87,7 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { mediaSessionLegacyHelperWrapper, backActionInteractor, powerInteractor, + kosmos.keyguardMediaKeyInteractor, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractorTest.kt new file mode 100644 index 000000000000..b82e1542903b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractorTest.kt @@ -0,0 +1,171 @@ +/* + * 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.domain.interactor + +import android.platform.test.annotations.EnableFlags +import android.view.KeyEvent +import android.view.KeyEvent.ACTION_DOWN +import android.view.KeyEvent.ACTION_UP +import android.view.KeyEvent.KEYCODE_MEDIA_PAUSE +import android.view.KeyEvent.KEYCODE_MEDIA_PLAY +import android.view.KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.testScope +import com.android.systemui.lifecycle.activateIn +import com.android.systemui.telephony.data.repository.fakeTelephonyRepository +import com.android.systemui.testKosmos +import com.android.systemui.volume.data.repository.fakeAudioRepository +import com.google.common.truth.Correspondence.transforming +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +@EnableFlags(FLAG_COMPOSE_BOUNCER) +class KeyguardMediaKeyInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + + private val underTest = kosmos.keyguardMediaKeyInteractor + + @Before + fun setup() { + underTest.activateIn(testScope) + } + + @Test + fun test_onKeyEvent_playPauseKeyEvents_areSkipped_whenACallIsActive() = + testScope.runTest { + kosmos.fakeTelephonyRepository.setIsInCall(true) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KEYCODE_MEDIA_PLAY)) + assertEventConsumed(KeyEvent(ACTION_DOWN, KEYCODE_MEDIA_PAUSE)) + assertEventConsumed(KeyEvent(ACTION_DOWN, KEYCODE_MEDIA_PLAY_PAUSE)) + + assertThat(kosmos.fakeAudioRepository.dispatchedKeyEvents).isEmpty() + } + + @Test + fun test_onKeyEvent_playPauseKeyEvents_areNotSkipped_whenACallIsNotActive() = + testScope.runTest { + kosmos.fakeTelephonyRepository.setIsInCall(false) + + assertEventNotConsumed(KeyEvent(ACTION_DOWN, KEYCODE_MEDIA_PAUSE)) + assertEventConsumed(KeyEvent(ACTION_UP, KEYCODE_MEDIA_PAUSE)) + assertEventNotConsumed(KeyEvent(ACTION_DOWN, KEYCODE_MEDIA_PLAY)) + assertEventConsumed(KeyEvent(ACTION_UP, KEYCODE_MEDIA_PLAY)) + assertEventNotConsumed(KeyEvent(ACTION_DOWN, KEYCODE_MEDIA_PLAY_PAUSE)) + assertEventConsumed(KeyEvent(ACTION_UP, KEYCODE_MEDIA_PLAY_PAUSE)) + + assertThat(kosmos.fakeAudioRepository.dispatchedKeyEvents) + .comparingElementsUsing<KeyEvent, Pair<Int, Int>>( + transforming({ Pair(it!!.action, it.keyCode) }, "action and keycode") + ) + .containsExactly( + Pair(ACTION_UP, KEYCODE_MEDIA_PAUSE), + Pair(ACTION_UP, KEYCODE_MEDIA_PLAY), + Pair(ACTION_UP, KEYCODE_MEDIA_PLAY_PAUSE), + ) + .inOrder() + } + + @Test + fun test_onKeyEvent_nonPlayPauseKeyEvents_areNotSkipped_whenACallIsActive() = + testScope.runTest { + kosmos.fakeTelephonyRepository.setIsInCall(true) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_MUTE)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_MUTE)) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_MEDIA_STOP)) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT)) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS)) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_REWIND)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_MEDIA_REWIND)) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_RECORD)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_MEDIA_RECORD)) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD)) + + assertEventConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK)) + assertEventConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK)) + + assertThat(kosmos.fakeAudioRepository.dispatchedKeyEvents) + .comparingElementsUsing<KeyEvent, Pair<Int, Int>>( + transforming({ Pair(it!!.action, it.keyCode) }, "action and keycode") + ) + .containsExactly( + Pair(ACTION_DOWN, KeyEvent.KEYCODE_MUTE), + Pair(ACTION_UP, KeyEvent.KEYCODE_MUTE), + Pair(ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK), + Pair(ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK), + Pair(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP), + Pair(ACTION_UP, KeyEvent.KEYCODE_MEDIA_STOP), + Pair(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT), + Pair(ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT), + Pair(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS), + Pair(ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS), + Pair(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_REWIND), + Pair(ACTION_UP, KeyEvent.KEYCODE_MEDIA_REWIND), + Pair(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_RECORD), + Pair(ACTION_UP, KeyEvent.KEYCODE_MEDIA_RECORD), + Pair(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD), + Pair(ACTION_UP, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD), + Pair(ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK), + Pair(ACTION_UP, KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK), + ) + .inOrder() + } + + @Test + fun volumeKeyEvents_keyEvents_areSkipped() = + testScope.runTest { + kosmos.fakeTelephonyRepository.setIsInCall(false) + + assertEventNotConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)) + assertEventNotConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)) + assertEventNotConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN)) + assertEventNotConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_VOLUME_DOWN)) + assertEventNotConsumed(KeyEvent(ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE)) + assertEventNotConsumed(KeyEvent(ACTION_UP, KeyEvent.KEYCODE_VOLUME_MUTE)) + } + + private fun assertEventConsumed(keyEvent: KeyEvent) { + assertThat(underTest.processMediaKeyEvent(keyEvent)).isTrue() + } + + private fun assertEventNotConsumed(keyEvent: KeyEvent) { + assertThat(underTest.processMediaKeyEvent(keyEvent)).isFalse() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt index 5bd3645b4cab..99d2da670172 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt @@ -30,9 +30,11 @@ import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.qs.pipeline.domain.interactor.panelInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileInput import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import com.android.systemui.recordissue.IssueRecordingState import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.screenrecord.RecordingController import com.android.systemui.settings.UserContextProvider +import com.android.systemui.settings.userFileManager import com.android.systemui.settings.userTracker import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.keyguardStateController @@ -81,10 +83,11 @@ class IssueRecordingUserActionInteractorTest : SysuiTestCase() { underTest = IssueRecordingUserActionInteractor( testDispatcher, + IssueRecordingState(userTracker, userFileManager), KeyguardDismissUtil( keyguardStateController, statusBarStateController, - activityStarter + activityStarter, ), keyguardStateController, dialogTransitionAnimator, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt index a1edfc1dbcd5..a3f81274dfc5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt @@ -107,7 +107,7 @@ class IssueRecordingServiceSessionTest : SysuiTestCase() { @Test fun requestBugreport_afterReceivingShareCommand_withTakeBugreportTrue() { - issueRecordingState.takeBugreport = true + underTest.takeBugReport = true val uri = mock<Uri>() underTest.share(0, uri) @@ -118,7 +118,7 @@ class IssueRecordingServiceSessionTest : SysuiTestCase() { @Test fun sharesTracesDirectly_afterReceivingShareCommand_withTakeBugreportFalse() { - issueRecordingState.takeBugreport = false + underTest.takeBugReport = false val uri = mock<Uri>() underTest.share(0, uri) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt deleted file mode 100644 index 28d53c72640f..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -package com.android.systemui.screenshot - -import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo - -internal class FakeScreenshotPolicy : ScreenshotPolicy { - - private val userTypes = mutableMapOf<Int, Boolean>() - private val contentInfo = mutableMapOf<Int, DisplayContentInfo?>() - - fun setManagedProfile(userId: Int, managedUser: Boolean) { - userTypes[userId] = managedUser - } - override suspend fun isManagedProfile(userId: Int): Boolean { - return userTypes[userId] ?: error("No managedProfile value set for userId $userId") - } - - fun setDisplayContentInfo(userId: Int, contentInfo: DisplayContentInfo) { - this.contentInfo[userId] = contentInfo - } - - override suspend fun findPrimaryContent(displayId: Int): DisplayContentInfo { - return contentInfo[displayId] ?: error("No DisplayContentInfo set for displayId $displayId") - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt index fb91c78b9041..080f46fd5f48 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock +import com.google.android.msdl.domain.MSDLPlayer import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before @@ -46,34 +47,26 @@ import org.mockito.Mockito.isNull import org.mockito.Mockito.never import org.mockito.Mockito.notNull import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) class BrightnessSliderControllerTest : SysuiTestCase() { - @Mock - private lateinit var brightnessSliderView: BrightnessSliderView - @Mock - private lateinit var enforcedAdmin: RestrictedLockUtils.EnforcedAdmin - @Mock - private lateinit var mirrorController: BrightnessMirrorController - @Mock - private lateinit var mirror: ToggleSlider - @Mock - private lateinit var motionEvent: MotionEvent - @Mock - private lateinit var listener: ToggleSlider.Listener - @Mock - private lateinit var vibratorHelper: VibratorHelper - @Mock - private lateinit var activityStarter: ActivityStarter + @Mock private lateinit var brightnessSliderView: BrightnessSliderView + @Mock private lateinit var enforcedAdmin: RestrictedLockUtils.EnforcedAdmin + @Mock private lateinit var mirrorController: BrightnessMirrorController + @Mock private lateinit var mirror: ToggleSlider + @Mock private lateinit var motionEvent: MotionEvent + @Mock private lateinit var listener: ToggleSlider.Listener + @Mock private lateinit var vibratorHelper: VibratorHelper + @Mock private lateinit var msdlPlayer: MSDLPlayer + @Mock private lateinit var activityStarter: ActivityStarter @Captor private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener> - @Mock - private lateinit var seekBar: SeekBar + @Mock private lateinit var seekBar: SeekBar private val uiEventLogger = UiEventLoggerFake() private var mFalsingManager: FalsingManagerFake = FalsingManagerFake() private val systemClock = FakeSystemClock() @@ -93,7 +86,7 @@ class BrightnessSliderControllerTest : SysuiTestCase() { brightnessSliderView, mFalsingManager, uiEventLogger, - SeekbarHapticPlugin(vibratorHelper, systemClock), + SeekbarHapticPlugin(vibratorHelper, msdlPlayer, systemClock), activityStarter, ) mController.init() @@ -241,4 +234,4 @@ class BrightnessSliderControllerTest : SysuiTestCase() { assertThat(uiEventLogger.numLogs()).isEqualTo(1) assertThat(uiEventLogger.eventId(0)).isEqualTo(event.id) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt deleted file mode 100644 index 2ac0ed0efff4..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt +++ /dev/null @@ -1,185 +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.shade - -import android.os.PowerManager -import android.view.MotionEvent -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.classifier.FalsingCollector -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.plugins.FalsingManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.power.data.repository.FakePowerRepository -import com.android.systemui.power.domain.interactor.PowerInteractorFactory -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@SmallTest -@OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidJUnit4::class) -class LockscreenHostedDreamGestureListenerTest : SysuiTestCase() { - @Mock private lateinit var falsingManager: FalsingManager - @Mock private lateinit var falsingCollector: FalsingCollector - @Mock private lateinit var statusBarStateController: StatusBarStateController - @Mock private lateinit var shadeLogger: ShadeLogger - @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor - - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) - - private lateinit var powerRepository: FakePowerRepository - private lateinit var keyguardRepository: FakeKeyguardRepository - private lateinit var underTest: LockscreenHostedDreamGestureListener - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - - powerRepository = FakePowerRepository() - keyguardRepository = FakeKeyguardRepository() - - underTest = - LockscreenHostedDreamGestureListener( - falsingManager, - PowerInteractorFactory.create( - repository = powerRepository, - statusBarStateController = statusBarStateController, - ) - .powerInteractor, - statusBarStateController, - primaryBouncerInteractor, - keyguardRepository, - shadeLogger, - ) - whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) - whenever(primaryBouncerInteractor.isBouncerShowing()).thenReturn(false) - } - - @Test - fun testGestureDetector_onSingleTap_whileDreaming() = - testScope.runTest { - // GIVEN device dreaming and the dream is hosted in lockscreen - whenever(statusBarStateController.isDreaming).thenReturn(true) - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - testScope.runCurrent() - - // GIVEN the falsing manager does NOT think the tap is a false tap - whenever(falsingManager.isFalseTap(ArgumentMatchers.anyInt())).thenReturn(false) - - // WHEN there's a tap - underTest.onSingleTapUp(upEv) - - // THEN wake up device if dreaming - Truth.assertThat(powerRepository.lastWakeWhy).isNotNull() - Truth.assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_TAP) - } - - @Test - fun testGestureDetector_onSingleTap_notOnKeyguard() = - testScope.runTest { - // GIVEN device dreaming and the dream is hosted in lockscreen - whenever(statusBarStateController.isDreaming).thenReturn(true) - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - testScope.runCurrent() - - // GIVEN shade is open - whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE) - - // GIVEN the falsing manager does NOT think the tap is a false tap - whenever(falsingManager.isFalseTap(ArgumentMatchers.anyInt())).thenReturn(false) - - // WHEN there's a tap - underTest.onSingleTapUp(upEv) - - // THEN the falsing manager never gets a call - verify(falsingManager, never()).isFalseTap(ArgumentMatchers.anyInt()) - } - - @Test - fun testGestureDetector_onSingleTap_bouncerShown() = - testScope.runTest { - // GIVEN device dreaming and the dream is hosted in lockscreen - whenever(statusBarStateController.isDreaming).thenReturn(true) - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - testScope.runCurrent() - - // GIVEN bouncer is expanded - whenever(primaryBouncerInteractor.isBouncerShowing()).thenReturn(true) - - // GIVEN the falsing manager does NOT think the tap is a false tap - whenever(falsingManager.isFalseTap(ArgumentMatchers.anyInt())).thenReturn(false) - - // WHEN there's a tap - underTest.onSingleTapUp(upEv) - - // THEN the falsing manager never gets a call - verify(falsingManager, never()).isFalseTap(ArgumentMatchers.anyInt()) - } - - @Test - fun testGestureDetector_onSingleTap_falsing() = - testScope.runTest { - // GIVEN device dreaming and the dream is hosted in lockscreen - whenever(statusBarStateController.isDreaming).thenReturn(true) - keyguardRepository.setIsActiveDreamLockscreenHosted(true) - testScope.runCurrent() - - // GIVEN the falsing manager thinks the tap is a false tap - whenever(falsingManager.isFalseTap(ArgumentMatchers.anyInt())).thenReturn(true) - - // WHEN there's a tap - underTest.onSingleTapUp(upEv) - - // THEN the device doesn't wake up - Truth.assertThat(powerRepository.lastWakeWhy).isNull() - Truth.assertThat(powerRepository.lastWakeReason).isNull() - } - - @Test - fun testSingleTap_notDreaming_noFalsingCheck() = - testScope.runTest { - // GIVEN device not dreaming with lockscreen hosted dream - whenever(statusBarStateController.isDreaming).thenReturn(false) - keyguardRepository.setIsActiveDreamLockscreenHosted(false) - testScope.runCurrent() - - // WHEN there's a tap - underTest.onSingleTapUp(upEv) - - // THEN the falsing manager never gets a call - verify(falsingManager, never()).isFalseTap(ArgumentMatchers.anyInt()) - } -} - -private val upEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt index 663c3418b144..16da3d22f4f7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt @@ -70,14 +70,14 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { } private fun createController() = - PrivacyDotViewController( + PrivacyDotViewControllerImpl( executor, testScope.backgroundScope, statusBarStateController, configurationController, contentInsetsProvider, animationScheduler = mock<SystemStatusAnimationScheduler>(), - shadeInteractor = null + shadeInteractor = null, ) .also { it.setUiExecutor(executor) } @@ -307,7 +307,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() { newTopLeftView, newTopRightView, newBottomLeftView, - newBottomRightView + newBottomRightView, ) assertThat((newBottomRightView.layoutParams as FrameLayout.LayoutParams).gravity) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java index dc9c22f566bf..f1edb417a314 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java @@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE; import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer; import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -36,9 +37,11 @@ import static org.mockito.Mockito.when; import static java.util.Objects.requireNonNull; +import android.app.Flags; import android.database.ContentObserver; import android.os.Handler; import android.os.RemoteException; +import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import androidx.annotation.NonNull; @@ -61,6 +64,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.NotifInf import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider; import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; @@ -69,6 +73,8 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionSt import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; +import com.android.systemui.statusbar.notification.row.icon.AppIconProvider; +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.util.settings.SecureSettings; @@ -82,10 +88,12 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; @SmallTest @RunWith(AndroidJUnit4.class) @@ -93,6 +101,7 @@ import java.util.Map; public class PreparationCoordinatorTest extends SysuiTestCase { private NotifCollectionListener mCollectionListener; private OnBeforeFinalizeFilterListener mBeforeFilterListener; + private OnBeforeTransformGroupsListener mBeforeTransformGroupsListener; private NotifFilter mUninflatedFilter; private NotifFilter mInflationErrorFilter; private NotifInflationErrorManager mErrorManager; @@ -101,6 +110,8 @@ public class PreparationCoordinatorTest extends SysuiTestCase { @Captor private ArgumentCaptor<NotifCollectionListener> mCollectionListenerCaptor; @Captor private ArgumentCaptor<OnBeforeFinalizeFilterListener> mBeforeFilterListenerCaptor; + @Captor private ArgumentCaptor<OnBeforeTransformGroupsListener> + mBeforeTransformGroupsListenerCaptor; @Captor private ArgumentCaptor<NotifInflater.Params> mParamsCaptor; @Mock private NotifSectioner mNotifSectioner; @@ -108,13 +119,14 @@ public class PreparationCoordinatorTest extends SysuiTestCase { @Mock private NotifPipeline mNotifPipeline; @Mock private IStatusBarService mService; @Mock private BindEventManagerImpl mBindEventManagerImpl; + @Mock private AppIconProvider mAppIconProvider; + @Mock private NotificationIconStyleProvider mNotificationIconStyleProvider; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private SensitiveNotificationProtectionController mSensitiveNotifProtectionController; @Mock private Handler mHandler; @Mock private SecureSettings mSecureSettings; @Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater(); - @Mock - HighPriorityProvider mHighPriorityProvider; + @Mock HighPriorityProvider mHighPriorityProvider; private SectionStyleProvider mSectionStyleProvider; @Mock private UserTracker mUserTracker; @Mock private GroupMembershipManager mGroupMembershipManager; @@ -126,6 +138,11 @@ public class PreparationCoordinatorTest extends SysuiTestCase { return new NotificationEntryBuilder().setSection(mNotifSection); } + @NonNull + private GroupEntryBuilder getGroupEntryBuilder() { + return new GroupEntryBuilder().setSection(mNotifSection); + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -138,7 +155,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase { mSectionStyleProvider, mUserTracker, mGroupMembershipManager - ); + ); mEntry = getNotificationEntryBuilder().setParent(ROOT_ENTRY).build(); mInflationError = new Exception(TEST_MESSAGE); mErrorManager = new NotifInflationErrorManager(); @@ -153,6 +170,8 @@ public class PreparationCoordinatorTest extends SysuiTestCase { mAdjustmentProvider, mService, mBindEventManagerImpl, + mAppIconProvider, + mNotificationIconStyleProvider, TEST_CHILD_BIND_CUTOFF, TEST_MAX_GROUP_DELAY); @@ -163,6 +182,15 @@ public class PreparationCoordinatorTest extends SysuiTestCase { mInflationErrorFilter = filters.get(0); mUninflatedFilter = filters.get(1); + if (android.app.Flags.notificationsRedesignAppIcons()) { + verify(mNotifPipeline).addOnBeforeTransformGroupsListener( + mBeforeTransformGroupsListenerCaptor.capture()); + mBeforeTransformGroupsListener = mBeforeTransformGroupsListenerCaptor.getValue(); + } else { + verify(mNotifPipeline, never()).addOnBeforeTransformGroupsListener( + mBeforeTransformGroupsListenerCaptor.capture()); + } + verify(mNotifPipeline).addCollectionListener(mCollectionListenerCaptor.capture()); mCollectionListener = mCollectionListenerCaptor.getValue(); @@ -199,6 +227,100 @@ public class PreparationCoordinatorTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_NOTIFICATIONS_REDESIGN_APP_ICONS) + public void testPurgesAppIconProviderCache() { + // GIVEN a notification list + NotificationEntry entry1 = getNotificationEntryBuilder().setPkg("1").build(); + NotificationEntry entry2 = getNotificationEntryBuilder().setPkg("2").build(); + NotificationEntry entry2bis = getNotificationEntryBuilder().setPkg("2").build(); + NotificationEntry entry3 = getNotificationEntryBuilder().setPkg("3").build(); + + String groupKey1 = "group1"; + NotificationEntry summary = + getNotificationEntryBuilder() + .setPkg(groupKey1) + .setGroup(mContext, groupKey1) + .setGroupSummary(mContext, true) + .build(); + NotificationEntry child1 = getNotificationEntryBuilder().setGroup(mContext, groupKey1) + .setPkg(groupKey1).build(); + NotificationEntry child2 = getNotificationEntryBuilder().setGroup(mContext, groupKey1) + .setPkg(groupKey1).build(); + GroupEntry groupWithSummaryAndChildren = getGroupEntryBuilder().setKey(groupKey1) + .setSummary(summary).addChild(child1).addChild(child2).build(); + + String groupKey2 = "group2"; + NotificationEntry summary2 = + getNotificationEntryBuilder() + .setPkg(groupKey2) + .setGroup(mContext, groupKey2) + .setGroupSummary(mContext, true) + .build(); + GroupEntry summaryOnlyGroup = getGroupEntryBuilder().setKey(groupKey2) + .setSummary(summary2).build(); + + // WHEN onBeforeTransformGroup is called + mBeforeTransformGroupsListener.onBeforeTransformGroups( + List.of(entry1, entry2, entry2bis, entry3, + groupWithSummaryAndChildren, summaryOnlyGroup)); + + // THEN purge should be called + ArgumentCaptor<Collection<String>> argumentCaptor = ArgumentCaptor.forClass(List.class); + verify(mAppIconProvider).purgeCache(argumentCaptor.capture()); + List<String> actualList = argumentCaptor.getValue().stream().sorted().toList(); + List<String> expectedList = Stream.of("1", "2", "3", "group1", "group2") + .sorted().toList(); + assertEquals(expectedList, actualList); + } + + @Test + @EnableFlags(Flags.FLAG_NOTIFICATIONS_REDESIGN_APP_ICONS) + public void testPurgesNotificationIconStyleProviderCache() { + // GIVEN a notification list + NotificationEntry entry1 = getNotificationEntryBuilder().setPkg("1").build(); + NotificationEntry entry2 = getNotificationEntryBuilder().setPkg("2").build(); + NotificationEntry entry2bis = getNotificationEntryBuilder().setPkg("2").build(); + NotificationEntry entry3 = getNotificationEntryBuilder().setPkg("3").build(); + + String groupKey1 = "group1"; + NotificationEntry summary = + getNotificationEntryBuilder() + .setPkg(groupKey1) + .setGroup(mContext, groupKey1) + .setGroupSummary(mContext, true) + .build(); + NotificationEntry child1 = getNotificationEntryBuilder().setGroup(mContext, groupKey1) + .setPkg(groupKey1).build(); + NotificationEntry child2 = getNotificationEntryBuilder().setGroup(mContext, groupKey1) + .setPkg(groupKey1).build(); + GroupEntry groupWithSummaryAndChildren = getGroupEntryBuilder().setKey(groupKey1) + .setSummary(summary).addChild(child1).addChild(child2).build(); + + String groupKey2 = "group2"; + NotificationEntry summary2 = + getNotificationEntryBuilder() + .setPkg(groupKey2) + .setGroup(mContext, groupKey2) + .setGroupSummary(mContext, true) + .build(); + GroupEntry summaryOnlyGroup = getGroupEntryBuilder().setKey(groupKey2) + .setSummary(summary2).build(); + + // WHEN onBeforeTransformGroup is called + mBeforeTransformGroupsListener.onBeforeTransformGroups( + List.of(entry1, entry2, entry2bis, entry3, + groupWithSummaryAndChildren, summaryOnlyGroup)); + + // THEN purge should be called + ArgumentCaptor<Collection<String>> argumentCaptor = ArgumentCaptor.forClass(List.class); + verify(mNotificationIconStyleProvider).purgeCache(argumentCaptor.capture()); + List<String> actualList = argumentCaptor.getValue().stream().sorted().toList(); + List<String> expectedList = Stream.of("1", "2", "3", "group1", "group2") + .sorted().toList(); + assertEquals(expectedList, actualList); + } + + @Test public void testInflatesNewNotification() { // WHEN there is a new notification mCollectionListener.onEntryInit(mEntry); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java index aed9af6df454..406ca0585dd9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java @@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.keyguard.logging.KeyguardUpdateMonitorLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; @@ -67,6 +68,8 @@ public class KeyguardStateControllerTest extends SysuiTestCase { @Mock private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy; @Mock + private Lazy<KeyguardInteractor> mKeyguardInteractorLazy; + @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock private KeyguardUpdateMonitorLogger mLogger; @@ -86,6 +89,7 @@ public class KeyguardStateControllerTest extends SysuiTestCase { mKeyguardUnlockAnimationControllerLazy, mLogger, mDumpManager, + mKeyguardInteractorLazy, mFeatureFlags, mSelectedUserInteractor); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt new file mode 100644 index 000000000000..7d5559933cd8 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt @@ -0,0 +1,99 @@ +/* + * 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.volume.dialog.ringer.domain + +import android.media.AudioManager.RINGER_MODE_NORMAL +import android.media.AudioManager.RINGER_MODE_SILENT +import android.media.AudioManager.RINGER_MODE_VIBRATE +import android.media.AudioManager.STREAM_RING +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.settingslib.volume.shared.model.RingerMode +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.fakeVolumeDialogController +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class VolumeDialogRingerInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val controller = kosmos.fakeVolumeDialogController + + private lateinit var underTest: VolumeDialogRingerInteractor + + @Before + fun setUp() { + underTest = kosmos.volumeDialogRingerInteractor + controller.setStreamVolume(STREAM_RING, 50) + } + + @Test + fun setRingerMode_normal() = + testScope.runTest { + runCurrent() + val ringerModel by collectLastValue(underTest.ringerModel) + + underTest.setRingerMode(RingerMode(RINGER_MODE_NORMAL)) + controller.getState() + runCurrent() + + assertThat(ringerModel).isNotNull() + assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_NORMAL)) + } + + @Test + fun setRingerMode_silent() = + testScope.runTest { + runCurrent() + val ringerModel by collectLastValue(underTest.ringerModel) + + underTest.setRingerMode(RingerMode(RINGER_MODE_SILENT)) + controller.getState() + runCurrent() + + assertThat(ringerModel).isNotNull() + assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_SILENT)) + } + + @Test + fun setRingerMode_vibrate() = + testScope.runTest { + runCurrent() + val ringerModel by collectLastValue(underTest.ringerModel) + + underTest.setRingerMode(RingerMode(RINGER_MODE_VIBRATE)) + controller.getState() + runCurrent() + + assertThat(ringerModel).isNotNull() + assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_VIBRATE)) + } +} diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index cdf15ca83dd9..c494e8525e0f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3745,6 +3745,10 @@ use. The helper shows shortcuts in categories, which can be collapsed or expanded. [CHAR LIMIT=NONE] --> <string name="shortcut_helper_content_description_drag_handle">Drag handle</string> + <!-- Label on the keyboard settings button in keyboard shortcut helper, that allows user to + open keyboard settings while in shortcut helper. The helper is a component that shows the + user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> + <string name="shortcut_helper_keyboard_settings_buttons_label">Keyboard Settings</string> <!-- Keyboard touchpad tutorial scheduler--> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 83ab5245bc31..b3ea75d00917 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2368,7 +2368,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } // Take a guess at initial SIM state, battery status and PLMN until we get an update - mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, /* level= */ 100, /* plugged= */ + mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, /* level= */ -1, /* plugged= */ 0, CHARGING_POLICY_DEFAULT, /* maxChargingWattage= */0, /* present= */true); // Watch for interesting updates diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 8ae11abab473..811b47d57c1d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -43,7 +43,7 @@ import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dump.DumpManager; import com.android.systemui.process.ProcessWrapper; import com.android.systemui.res.R; -import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.util.NotificationChannels; import java.lang.reflect.InvocationTargetException; @@ -454,13 +454,13 @@ public class SystemUIApplication extends Application implements @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { if (mServicesStarted) { - ConfigurationController configController = mSysUIComponent.getConfigurationController(); + ConfigurationForwarder configForwarder = mSysUIComponent.getConfigurationForwarder(); if (Trace.isEnabled()) { Trace.traceBegin( Trace.TRACE_TAG_APP, - configController.getClass().getSimpleName() + ".onConfigurationChanged()"); + configForwarder.getClass().getSimpleName() + ".onConfigurationChanged()"); } - configController.onConfigurationChanged(newConfig); + configForwarder.onConfigurationChanged(newConfig); Trace.endSection(); } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/startable/BouncerStartable.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/startable/BouncerStartable.kt new file mode 100644 index 000000000000..e3cf88c94afd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/startable/BouncerStartable.kt @@ -0,0 +1,43 @@ +/* + * 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.bouncer.domain.startable + +import com.android.app.tracing.coroutines.launchTraced +import com.android.systemui.CoreStartable +import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.KeyguardMediaKeyInteractor +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope + +/** Starts interactors needed for the compose bouncer to work as expected. */ +@SysUISingleton +class BouncerStartable +@Inject +constructor( + private val keyguardMediaKeyInteractor: dagger.Lazy<KeyguardMediaKeyInteractor>, + @Application private val scope: CoroutineScope, +) : CoreStartable { + override fun start() { + if (!ComposeBouncerFlags.isEnabled) return + + scope.launchTraced("KeyguardMediaKeyInteractor#start") { + keyguardMediaKeyInteractor.get().activate() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt index 7647cf6081bf..47570a53d663 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt @@ -43,17 +43,25 @@ object ComposeBouncerFlags { fun isUnexpectedlyInLegacyMode() = RefactorFlagUtils.isUnexpectedlyInLegacyMode( isEnabled, - "SceneContainerFlag || ComposeBouncerFlag" + "SceneContainerFlag || ComposeBouncerFlag", ) /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + fun assertInLegacyMode() = + RefactorFlagUtils.assertInLegacyMode(isEnabled, "SceneContainerFlag || ComposeBouncerFlag") + + /** * Returns `true` if only compose bouncer is enabled and scene container framework is not * enabled. */ @Deprecated( "Avoid using this, this is meant to be used only by the glue code " + "that includes compose bouncer in legacy keyguard.", - replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()") + replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()"), ) fun isOnlyComposeBouncerEnabled(): Boolean { return !SceneContainerFlag.isEnabled && Flags.composeBouncer() diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index 148b9ea61e2c..1bcc1eeb4f12 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.bouncer.ui.viewmodel import android.annotation.StringRes +import androidx.compose.ui.input.key.KeyEventType import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor @@ -122,6 +123,13 @@ sealed class AuthMethodBouncerViewModel( /** Invoked after a successful authentication. */ protected open fun onSuccessfulAuthentication() = Unit + /** + * Invoked for any key events on the bouncer. + * + * @return whether the event was consumed by this method and should not be propagated further. + */ + open fun onKeyEvent(type: KeyEventType, keyCode: Int): Boolean = false + /** Perform authentication result haptics */ private fun performAuthenticationHapticFeedback(result: AuthenticationResult) { if (result == AuthenticationResult.SKIPPED) return diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt index 5f973917440c..67d312e17069 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt @@ -34,6 +34,7 @@ import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.KeyguardMediaKeyInteractor import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel import dagger.assisted.AssistedFactory @@ -63,6 +64,7 @@ constructor( private val patternViewModelFactory: PatternBouncerViewModel.Factory, private val passwordViewModelFactory: PasswordBouncerViewModel.Factory, private val bouncerHapticPlayer: BouncerHapticPlayer, + private val keyguardMediaKeyInteractor: KeyguardMediaKeyInteractor, ) : ExclusiveActivatable() { private val _selectedUserImage = MutableStateFlow<Bitmap?>(null) val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow() @@ -337,10 +339,9 @@ constructor( * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise. */ fun onKeyEvent(keyEvent: KeyEvent): Boolean { - return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent( - keyEvent.type, - keyEvent.nativeKeyEvent.keyCode, - ) ?: false + if (keyguardMediaKeyInteractor.processMediaKeyEvent(keyEvent.nativeKeyEvent)) return true + return authMethodViewModel.value?.onKeyEvent(keyEvent.type, keyEvent.nativeKeyEvent.keyCode) + ?: false } data class DialogViewModel( diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt index 1427d787ea86..b8c6ab3783d5 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt @@ -16,9 +16,12 @@ package com.android.systemui.bouncer.ui.viewmodel +import android.view.KeyEvent import androidx.annotation.VisibleForTesting +import androidx.compose.ui.input.key.KeyEventType import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor +import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags import com.android.systemui.inputmethod.domain.interactor.InputMethodInteractor import com.android.systemui.res.R import com.android.systemui.user.domain.interactor.SelectedUserInteractor @@ -150,6 +153,17 @@ constructor( return _password.value.toCharArray().toList() } + override fun onKeyEvent(type: KeyEventType, keyCode: Int): Boolean { + // Ignore SPACE as a confirm key to allow the space character within passwords. + val isKeyboardEnterKey = + KeyEvent.isConfirmKey(keyCode) && + keyCode != KeyEvent.KEYCODE_SPACE && + type == KeyEventType.KeyUp + // consume confirm key events while on the bouncer. This prevents it from propagating + // and avoids other parent elements from receiving it. + return isKeyboardEnterKey && ComposeBouncerFlags.isOnlyComposeBouncerEnabled() + } + override fun onSuccessfulAuthentication() { wasSuccessfullyAuthenticated = true } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index 0cb4260e4d7f..5c8a9a692baf 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -251,7 +251,7 @@ constructor( * * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise. */ - fun onKeyEvent(type: KeyEventType, keyCode: Int): Boolean { + override fun onKeyEvent(type: KeyEventType, keyCode: Int): Boolean { return when (type) { KeyEventType.KeyUp -> { if (isConfirmKey(keyCode)) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 3fe6669de556..17f1961e662c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -29,6 +29,7 @@ import com.android.systemui.people.PeopleProvider; import com.android.systemui.startable.Dependencies; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; @@ -125,13 +126,20 @@ public interface SysUIComponent { BootCompleteCacheImpl provideBootCacheImpl(); /** - * Creates a ContextComponentHelper. + * Creates a ConfigurationController. */ @SysUISingleton @GlobalConfig ConfigurationController getConfigurationController(); /** + * Creates a ConfigurationForwarder. + */ + @SysUISingleton + @GlobalConfig + ConfigurationForwarder getConfigurationForwarder(); + + /** * Creates a ContextComponentHelper. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 0de919deb943..6fb6236a4ed3 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -23,6 +23,7 @@ import com.android.systemui.SliceBroadcastRelayHandler import com.android.systemui.accessibility.Magnification import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.BiometricNotificationService +import com.android.systemui.bouncer.domain.startable.BouncerStartable import com.android.systemui.clipboardoverlay.ClipboardListener import com.android.systemui.controls.dagger.StartControlsStartableModule import com.android.systemui.dagger.qualifiers.PerUser @@ -304,6 +305,11 @@ abstract class SystemUICoreStartableModule { @Binds @IntoMap + @ClassKey(BouncerStartable::class) + abstract fun bindBouncerStartable(impl: BouncerStartable): CoreStartable + + @Binds + @IntoMap @ClassKey(KeyguardDismissBinder::class) abstract fun bindKeyguardDismissBinder(impl: KeyguardDismissBinder): CoreStartable diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 21922ff22afe..12718e8bd119 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -17,6 +17,7 @@ package com.android.systemui.doze; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.Flags.screenOffUnlockUdfps; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; @@ -248,8 +249,8 @@ public class DozeSensors { true /* touchscreen */, false /* ignoresSetting */, dozeParameters.longPressUsesProx(), - false /* immediatelyReRegister */, - true /* requiresAod */ + screenOffUnlockUdfps() /* immediatelyReRegister */, + !screenOffUnlockUdfps() /* requiresAod */ ), new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY), diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 95cd9eb4ae4b..61832875dc2e 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -165,7 +165,7 @@ object Flags { val QS_USER_DETAIL_SHORTCUT = resourceBooleanFlag( R.bool.flag_lockscreen_qs_user_detail_shortcut, - "qs_user_detail_shortcut" + "qs_user_detail_shortcut", ) // TODO(b/254512383): Tracking Bug @@ -365,11 +365,6 @@ object Flags { val ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD = releasedFlag("zj_285570694_lockscreen_transition_from_aod") - // 3000 - dream - // TODO(b/285059790) : Tracking Bug - @JvmField - val LOCKSCREEN_WALLPAPER_DREAM_ENABLED = unreleasedFlag("enable_lockscreen_wallpaper_dream") - // TODO(b/283447257): Tracking bug @JvmField val BIGPICTURE_NOTIFICATION_LAZY_LOADING = diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt index 932e5af6244b..cc77f68a801e 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt @@ -22,6 +22,7 @@ import android.widget.SeekBar import androidx.annotation.VisibleForTesting import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.time.SystemClock +import com.google.android.msdl.domain.MSDLPlayer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -39,6 +40,7 @@ class SeekbarHapticPlugin @JvmOverloads constructor( vibratorHelper: VibratorHelper, + msdlPlayer: MSDLPlayer, systemClock: SystemClock, sliderHapticFeedbackConfig: SliderHapticFeedbackConfig = SliderHapticFeedbackConfig(), private val sliderTrackerConfig: SeekableSliderTrackerConfig = SeekableSliderTrackerConfig(), @@ -63,6 +65,7 @@ constructor( private val sliderHapticFeedbackProvider = SliderHapticFeedbackProvider( vibratorHelper, + msdlPlayer, dragVelocityProvider, sliderHapticFeedbackConfig, systemClock, diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt index 06428b77f759..bc4f531b8b81 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt @@ -22,7 +22,11 @@ import android.view.VelocityTracker import android.view.animation.AccelerateInterpolator import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting +import com.android.systemui.Flags import com.android.systemui.statusbar.VibratorHelper +import com.google.android.msdl.data.model.MSDLToken +import com.google.android.msdl.domain.InteractionProperties +import com.google.android.msdl.domain.MSDLPlayer import kotlin.math.abs import kotlin.math.min import kotlin.math.pow @@ -38,6 +42,7 @@ import kotlin.math.pow */ class SliderHapticFeedbackProvider( private val vibratorHelper: VibratorHelper, + private val msdlPlayer: MSDLPlayer, private val velocityProvider: SliderDragVelocityProvider, private val config: SliderHapticFeedbackConfig = SliderHapticFeedbackConfig(), private val clock: com.android.systemui.util.time.SystemClock, @@ -67,11 +72,20 @@ class SliderHapticFeedbackProvider( */ private fun vibrateOnEdgeCollision(absoluteVelocity: Float) { val powerScale = scaleOnEdgeCollision(absoluteVelocity) - val vibration = - VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, powerScale) - .compose() - vibratorHelper.vibrate(vibration, VIBRATION_ATTRIBUTES_PIPELINING) + if (Flags.msdlFeedback()) { + val properties = + InteractionProperties.DynamicVibrationScale( + powerScale, + VIBRATION_ATTRIBUTES_PIPELINING, + ) + msdlPlayer.playToken(MSDLToken.DRAG_THRESHOLD_INDICATOR_LIMIT, properties) + } else { + val vibration = + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, powerScale) + .compose() + vibratorHelper.vibrate(vibration, VIBRATION_ATTRIBUTES_PIPELINING) + } } /** @@ -112,16 +126,26 @@ class SliderHapticFeedbackProvider( val powerScale = scaleOnDragTexture(absoluteVelocity, normalizedSliderProgress) - // Trigger the vibration composition - val composition = VibrationEffect.startComposition() - repeat(config.numberOfLowTicks) { - composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, powerScale) - } - vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING) + // Deliver haptic feedback + performContinuousSliderDragVibration(powerScale) dragTextureLastTime = currentTime dragTextureLastProgress = normalizedSliderProgress } + private fun performContinuousSliderDragVibration(scale: Float) { + if (Flags.msdlFeedback()) { + val properties = + InteractionProperties.DynamicVibrationScale(scale, VIBRATION_ATTRIBUTES_PIPELINING) + msdlPlayer.playToken(MSDLToken.DRAG_INDICATOR_CONTINUOUS, properties) + } else { + val composition = VibrationEffect.startComposition() + repeat(config.numberOfLowTicks) { + composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, scale) + } + vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING) + } + } + /** * Get the scale of the drag texture vibration. * diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt index 1dbcb3dfe399..de242597f463 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt @@ -33,6 +33,7 @@ import com.android.systemui.haptics.slider.SliderStateTracker import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.time.SystemClock +import com.google.android.msdl.domain.MSDLPlayer import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -50,6 +51,7 @@ constructor( @Assisted private val sliderHapticFeedbackConfig: SliderHapticFeedbackConfig, @Assisted private val sliderTrackerConfig: SeekableSliderTrackerConfig, vibratorHelper: VibratorHelper, + msdlPlayer: MSDLPlayer, systemClock: SystemClock, ) : ExclusiveActivatable() { @@ -78,6 +80,7 @@ constructor( private val sliderHapticFeedbackProvider = SliderHapticFeedbackProvider( vibratorHelper, + msdlPlayer, dragVelocityProvider, sliderHapticFeedbackConfig, systemClock, diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt index 3c8bb09ddc09..5cade686ae09 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt @@ -16,10 +16,7 @@ package com.android.systemui.keyboard.shortcut.ui.composable -import android.content.Context -import android.content.pm.PackageManager.NameNotFoundException import android.graphics.drawable.Icon -import android.util.Log import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.Image @@ -55,12 +52,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.OpenInNew -import androidx.compose.material.icons.filled.Apps import androidx.compose.material.icons.filled.ExpandMore -import androidx.compose.material.icons.filled.Keyboard import androidx.compose.material.icons.filled.Search -import androidx.compose.material.icons.filled.Tv -import androidx.compose.material.icons.filled.VerticalSplit import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider @@ -111,16 +104,15 @@ import androidx.compose.ui.util.fastForEachIndexed import com.android.compose.modifiers.thenIf import com.android.compose.ui.graphics.painter.rememberDrawablePainter import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory import com.android.systemui.keyboard.shortcut.ui.model.IconSource +import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState import com.android.systemui.res.R -import com.android.systemui.statusbar.phone.CentralSurfaces @Composable fun ShortcutHelper( @@ -187,7 +179,7 @@ private fun ActiveShortcutHelper( private fun ShortcutHelperSinglePane( searchQuery: String, onSearchQueryChanged: (String) -> Unit, - categories: List<ShortcutCategory>, + categories: List<ShortcutCategoryUi>, selectedCategoryType: ShortcutCategoryType?, onCategorySelected: (ShortcutCategoryType?) -> Unit, onKeyboardSettingsClicked: () -> Unit, @@ -228,7 +220,7 @@ private fun ShortcutHelperSinglePane( @Composable private fun CategoriesPanelSinglePane( searchQuery: String, - categories: List<ShortcutCategory>, + categories: List<ShortcutCategoryUi>, selectedCategoryType: ShortcutCategoryType?, onCategorySelected: (ShortcutCategoryType?) -> Unit, ) { @@ -267,7 +259,7 @@ private fun CategoriesPanelSinglePane( @Composable private fun CategoryItemSinglePane( searchQuery: String, - category: ShortcutCategory, + category: ShortcutCategoryUi, isExpanded: Boolean, onClick: () -> Unit, shape: Shape, @@ -278,9 +270,9 @@ private fun CategoryItemSinglePane( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().heightIn(min = 88.dp).padding(horizontal = 16.dp), ) { - ShortcutCategoryIcon(modifier = Modifier.size(24.dp), source = category.icon) + ShortcutCategoryIcon(modifier = Modifier.size(24.dp), source = category.iconSource) Spacer(modifier = Modifier.width(16.dp)) - Text(category.label(LocalContext.current)) + Text(category.label) Spacer(modifier = Modifier.weight(1f)) RotatingExpandCollapseIcon(isExpanded) } @@ -291,23 +283,6 @@ private fun CategoryItemSinglePane( } } -private val ShortcutCategory.icon: IconSource - @Composable - get() = - when (type) { - ShortcutCategoryType.System -> IconSource(imageVector = Icons.Default.Tv) - ShortcutCategoryType.MultiTasking -> - IconSource(imageVector = Icons.Default.VerticalSplit) - ShortcutCategoryType.InputMethodEditor -> - IconSource(imageVector = Icons.Default.Keyboard) - ShortcutCategoryType.AppCategories -> IconSource(imageVector = Icons.Default.Apps) - is ShortcutCategoryType.CurrentApp -> { - val context = LocalContext.current - val iconDrawable = context.packageManager.getApplicationIcon(type.packageName) - IconSource(painter = rememberDrawablePainter(drawable = iconDrawable)) - } - } - @Composable fun ShortcutCategoryIcon( source: IconSource, @@ -322,37 +297,6 @@ fun ShortcutCategoryIcon( } } -private fun ShortcutCategory.label(context: Context): String = - when (type) { - ShortcutCategoryType.System -> context.getString(R.string.shortcut_helper_category_system) - ShortcutCategoryType.MultiTasking -> - context.getString(R.string.shortcut_helper_category_multitasking) - ShortcutCategoryType.InputMethodEditor -> - context.getString(R.string.shortcut_helper_category_input) - ShortcutCategoryType.AppCategories -> - context.getString(R.string.shortcut_helper_category_app_shortcuts) - is ShortcutCategoryType.CurrentApp -> getApplicationLabelForCurrentApp(type, context) - } - -private fun getApplicationLabelForCurrentApp( - type: ShortcutCategoryType.CurrentApp, - context: Context, -): String { - val packageManagerForUser = CentralSurfaces.getPackageManagerForUser(context, context.userId) - return try { - val currentAppInfo = - packageManagerForUser.getApplicationInfoAsUser( - type.packageName, - /* flags = */ 0, - context.userId, - ) - packageManagerForUser.getApplicationLabel(currentAppInfo).toString() - } catch (e: NameNotFoundException) { - Log.wtf(ShortcutHelper.TAG, "Couldn't find app info by package name ${type.packageName}") - context.getString(R.string.shortcut_helper_category_current_app_shortcuts) - } -} - @Composable private fun RotatingExpandCollapseIcon(isExpanded: Boolean) { val expandIconRotationDegrees by @@ -384,7 +328,7 @@ private fun RotatingExpandCollapseIcon(isExpanded: Boolean) { } @Composable -private fun ShortcutCategoryDetailsSinglePane(searchQuery: String, category: ShortcutCategory) { +private fun ShortcutCategoryDetailsSinglePane(searchQuery: String, category: ShortcutCategoryUi) { Column(Modifier.padding(horizontal = 16.dp)) { category.subCategories.fastForEach { subCategory -> ShortcutSubCategorySinglePane(searchQuery, subCategory) @@ -409,7 +353,7 @@ private fun ShortcutHelperTwoPane( searchQuery: String, onSearchQueryChanged: (String) -> Unit, modifier: Modifier = Modifier, - categories: List<ShortcutCategory>, + categories: List<ShortcutCategoryUi>, selectedCategoryType: ShortcutCategoryType?, onCategorySelected: (ShortcutCategoryType?) -> Unit, onKeyboardSettingsClicked: () -> Unit, @@ -434,7 +378,7 @@ private fun ShortcutHelperTwoPane( } @Composable -private fun EndSidePanel(searchQuery: String, modifier: Modifier, category: ShortcutCategory?) { +private fun EndSidePanel(searchQuery: String, modifier: Modifier, category: ShortcutCategoryUi?) { val listState = rememberLazyListState() LaunchedEffect(key1 = category) { if (category != null) listState.animateScrollToItem(0) } if (category == null) { @@ -670,10 +614,10 @@ private fun textWithHighlightedSearchQuery(text: String, searchValue: String) = private fun StartSidePanel( onSearchQueryChanged: (String) -> Unit, modifier: Modifier, - categories: List<ShortcutCategory>, + categories: List<ShortcutCategoryUi>, onKeyboardSettingsClicked: () -> Unit, selectedCategory: ShortcutCategoryType?, - onCategoryClicked: (ShortcutCategory) -> Unit, + onCategoryClicked: (ShortcutCategoryUi) -> Unit, ) { Column(modifier) { ShortcutsSearchBar(onSearchQueryChanged) @@ -690,15 +634,15 @@ private fun StartSidePanel( @Composable private fun CategoriesPanelTwoPane( - categories: List<ShortcutCategory>, + categories: List<ShortcutCategoryUi>, selectedCategory: ShortcutCategoryType?, - onCategoryClicked: (ShortcutCategory) -> Unit, + onCategoryClicked: (ShortcutCategoryUi) -> Unit, ) { Column { categories.fastForEach { CategoryItemTwoPane( - label = it.label(LocalContext.current), - iconSource = it.icon, + label = it.label, + iconSource = it.iconSource, selected = selectedCategory == it.type, onClick = { onCategoryClicked(it) }, ) @@ -833,7 +777,7 @@ private fun KeyboardSettings(horizontalPadding: Dp, verticalPadding: Dp, onClick ) { Row(verticalAlignment = Alignment.CenterVertically) { Text( - "Keyboard Settings", + stringResource(id = R.string.shortcut_helper_keyboard_settings_buttons_label), color = MaterialTheme.colorScheme.onSurfaceVariant, fontSize = 16.sp, ) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt new file mode 100644 index 000000000000..f5d478b5855d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCategoryUi.kt @@ -0,0 +1,34 @@ +/* + * 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.keyboard.shortcut.ui.model + +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory + +data class ShortcutCategoryUi( + val label: String, + val iconSource: IconSource, + val type: ShortcutCategoryType, + val subCategories: List<ShortcutSubCategory>, +) { + constructor( + label: String, + iconSource: IconSource, + shortcutCategory: ShortcutCategory, + ) : this(label, iconSource, shortcutCategory.type, shortcutCategory.subCategories) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt index d2122b3f173a..8f23261c3ef4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt @@ -16,14 +16,13 @@ package com.android.systemui.keyboard.shortcut.ui.model -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType sealed interface ShortcutsUiState { data class Active( val searchQuery: String, - val shortcutCategories: List<ShortcutCategory>, + val shortcutCategories: List<ShortcutCategoryUi>, val defaultSelectedCategory: ShortcutCategoryType?, ) : ShortcutsUiState diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt index 04aa04d1f2af..20d09ede39e3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt @@ -17,6 +17,15 @@ package com.android.systemui.keyboard.shortcut.ui.viewmodel import android.app.role.RoleManager +import android.content.pm.PackageManager.NameNotFoundException +import android.util.Log +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Android +import androidx.compose.material.icons.filled.Apps +import androidx.compose.material.icons.filled.Keyboard +import androidx.compose.material.icons.filled.Tv +import androidx.compose.material.icons.filled.VerticalSplit +import com.android.compose.ui.graphics.painter.DrawablePainter import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor @@ -25,7 +34,10 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory +import com.android.systemui.keyboard.shortcut.ui.model.IconSource +import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -51,6 +63,7 @@ constructor( ) { private val searchQuery = MutableStateFlow("") + private val userContext = userTracker.createCurrentUserContext(userTracker.userContext) val shouldShow = categoriesInteractor.shortcutCategories @@ -68,9 +81,10 @@ constructor( val categoriesWithLauncherExcluded = excludeLauncherApp(categories) val filteredCategories = filterCategoriesBySearchQuery(query, categoriesWithLauncherExcluded) + val shortcutCategoriesUi = convertCategoriesModelToUiModel(filteredCategories) ShortcutsUiState.Active( searchQuery = query, - shortcutCategories = filteredCategories, + shortcutCategories = shortcutCategoriesUi, defaultSelectedCategory = getDefaultSelectedCategory(filteredCategories), ) } @@ -78,9 +92,73 @@ constructor( .stateIn( scope = backgroundScope, started = SharingStarted.Lazily, - initialValue = ShortcutsUiState.Inactive + initialValue = ShortcutsUiState.Inactive, ) + private fun convertCategoriesModelToUiModel( + categories: List<ShortcutCategory> + ): List<ShortcutCategoryUi> { + return categories.map { category -> + ShortcutCategoryUi( + label = getShortcutCategoryLabel(category.type), + iconSource = getShortcutCategoryIcon(category.type), + shortcutCategory = category, + ) + } + } + + private fun getShortcutCategoryIcon(type: ShortcutCategoryType): IconSource { + return when (type) { + ShortcutCategoryType.System -> IconSource(imageVector = Icons.Default.Tv) + ShortcutCategoryType.MultiTasking -> + IconSource(imageVector = Icons.Default.VerticalSplit) + ShortcutCategoryType.InputMethodEditor -> + IconSource(imageVector = Icons.Default.Keyboard) + ShortcutCategoryType.AppCategories -> IconSource(imageVector = Icons.Default.Apps) + is CurrentApp -> { + try { + val iconDrawable = + userContext.packageManager.getApplicationIcon(type.packageName) + IconSource(painter = DrawablePainter(drawable = iconDrawable)) + } catch (e: NameNotFoundException) { + Log.wtf( + "ShortcutHelperViewModel", + "Package not found when retrieving icon for ${type.packageName}", + ) + IconSource(imageVector = Icons.Default.Android) + } + } + } + } + + private fun getShortcutCategoryLabel(type: ShortcutCategoryType): String = + when (type) { + ShortcutCategoryType.System -> + userContext.getString(R.string.shortcut_helper_category_system) + ShortcutCategoryType.MultiTasking -> + userContext.getString(R.string.shortcut_helper_category_multitasking) + ShortcutCategoryType.InputMethodEditor -> + userContext.getString(R.string.shortcut_helper_category_input) + ShortcutCategoryType.AppCategories -> + userContext.getString(R.string.shortcut_helper_category_app_shortcuts) + is CurrentApp -> getApplicationLabelForCurrentApp(type) + } + + private fun getApplicationLabelForCurrentApp(type: CurrentApp): String { + try { + val packageManagerForUser = userContext.packageManager + val currentAppInfo = + packageManagerForUser.getApplicationInfo(type.packageName, /* flags= */ 0) + return packageManagerForUser.getApplicationLabel(currentAppInfo).toString() + } catch (e: NameNotFoundException) { + Log.wtf( + "ShortcutHelperViewModel", + "Package Not found when retrieving Label for ${type.packageName}", + ) + return "Current App" + } + } + private suspend fun excludeLauncherApp( categories: List<ShortcutCategory> ): List<ShortcutCategory> { @@ -111,7 +189,7 @@ constructor( private fun filterCategoriesBySearchQuery( query: String, - categories: List<ShortcutCategory> + categories: List<ShortcutCategory>, ): List<ShortcutCategory> { val lowerCaseTrimmedQuery = query.trim().lowercase() if (lowerCaseTrimmedQuery.isEmpty()) { @@ -132,7 +210,7 @@ constructor( private fun filterSubCategoriesBySearchQuery( subCategories: List<ShortcutSubCategory>, - query: String + query: String, ) = subCategories .map { subCategory -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index fbc76c587be2..60a306b3e245 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2975,7 +2975,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, @Override public void run() { Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable"); - if (DEBUG) Log.d(TAG, "keyguardGoingAway"); + Log.d(TAG, "keyguardGoingAwayRunnable"); mKeyguardViewControllerLazy.get().keyguardGoingAway(); int flags = 0; 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 821017418277..9e99a879be41 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 @@ -114,7 +114,7 @@ interface KeyguardRepository { "away' is isInTransitionToState(GONE), but consider using more specific flows " + "whenever possible." ) - val isKeyguardGoingAway: Flow<Boolean> + val isKeyguardGoingAway: MutableStateFlow<Boolean> /** * Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the @@ -184,9 +184,6 @@ interface KeyguardRepository { /** Observable for whether the device is dreaming with an overlay, see [DreamOverlayService] */ val isDreamingWithOverlay: Flow<Boolean> - /** Observable for device dreaming state and the active dream is hosted in lockscreen */ - val isActiveDreamLockscreenHosted: StateFlow<Boolean> - /** * Observable for the amount of doze we are currently in. * @@ -308,8 +305,6 @@ interface KeyguardRepository { fun setIsDozing(isDozing: Boolean) - fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean) - fun dozeTimeTick() fun showDismissibleKeyguard() @@ -637,9 +632,6 @@ constructor( private val _isQuickSettingsVisible = MutableStateFlow(false) override val isQuickSettingsVisible: Flow<Boolean> = _isQuickSettingsVisible.asStateFlow() - private val _isActiveDreamLockscreenHosted = MutableStateFlow(false) - override val isActiveDreamLockscreenHosted = _isActiveDreamLockscreenHosted.asStateFlow() - private val _shortcutAbsoluteTop = MutableStateFlow(0F) override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow() @@ -655,10 +647,6 @@ constructor( override fun onUnlockedChanged() { isKeyguardDismissible.value = keyguardStateController.isUnlocked } - - override fun onKeyguardGoingAwayChanged() { - isKeyguardGoingAway.value = keyguardStateController.isKeyguardGoingAway - } } keyguardStateController.addCallback(callback) @@ -698,10 +686,6 @@ constructor( _isQuickSettingsVisible.value = isVisible } - override fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean) { - _isActiveDreamLockscreenHosted.value = isLockscreenHosted - } - override fun setClockShouldBeCentered(shouldBeCentered: Boolean) { _clockShouldBeCentered.value = shouldBeCentered } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 8c7fe5f87a3f..0c2d5778079b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -131,12 +131,13 @@ constructor( .collect { (_, isCommunalAvailable, isIdleOnCommunal) -> val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value + val isKeyguardGoingAway = keyguardInteractor.isKeyguardGoingAway.value if (!deviceEntryInteractor.isLockscreenEnabled()) { if (!SceneContainerFlag.isEnabled) { startTransitionTo(KeyguardState.GONE) } - } else if (canDismissLockscreen()) { + } else if (canDismissLockscreen() || isKeyguardGoingAway) { if (!SceneContainerFlag.isEnabled) { startTransitionTo(KeyguardState.GONE) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt index 5b7eeddfb8e1..d18d6dce2e94 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt @@ -105,21 +105,13 @@ constructor( combine( shadeInteractor.isShadeLayoutWide, activeNotificationsInteractor.areAnyNotificationsPresent, - keyguardInteractor.isActiveDreamLockscreenHosted, isOnAod, headsUpNotificationInteractor.isHeadsUpOrAnimatingAway, keyguardInteractor.isDozing, - ) { - isShadeLayoutWide, - areAnyNotificationsPresent, - isActiveDreamLockscreenHosted, - isOnAod, - isHeadsUp, - isDozing -> + ) { isShadeLayoutWide, areAnyNotificationsPresent, isOnAod, isHeadsUp, isDozing -> when { !isShadeLayoutWide -> true !areAnyNotificationsPresent -> true - isActiveDreamLockscreenHosted -> true // Pulsing notification appears on the right. Move clock left to avoid overlap. isHeadsUp && isDozing -> false else -> isOnAod 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 6ecbc6175e40..0d5ad547f15f 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 @@ -189,9 +189,6 @@ constructor( /** Whether any dreaming is running, including the doze dream. */ val isDreamingAny: Flow<Boolean> = repository.isDreaming - /** Whether the system is dreaming and the active dream is hosted in lockscreen */ - val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted - /** Event for when the camera gesture is detected */ val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = repository.onCameraLaunchDetected.filter { it.type != CameraLaunchType.IGNORE } @@ -244,7 +241,7 @@ constructor( /** Whether the keyguard is going away. */ @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.GONE") - val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway + val isKeyguardGoingAway: StateFlow<Boolean> = repository.isKeyguardGoingAway.asStateFlow() /** Keyguard can be clipped at the top as the shade is dragged */ val topClippingBounds: Flow<Int?> by lazy { @@ -477,10 +474,6 @@ constructor( } } - fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean) { - repository.setIsActiveDreamLockscreenHosted(isLockscreenHosted) - } - /** Sets whether quick settings or quick-quick settings is visible. */ fun setQuickSettingsVisible(isVisible: Boolean) { repository.setQuickSettingsVisible(isVisible) @@ -549,6 +542,10 @@ constructor( repository.setShortcutAbsoluteTop(top) } + fun setIsKeyguardGoingAway(isGoingAway: Boolean) { + repository.isKeyguardGoingAway.value = isGoingAway + } + companion object { private const val TAG = "KeyguardInteractor" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt index fcf486b5696b..d4d7e75a8b41 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt @@ -20,6 +20,7 @@ import android.content.Context import android.media.AudioManager import android.view.KeyEvent import com.android.systemui.back.domain.interactor.BackActionInteractor +import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler.Companion.handleAction import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper @@ -45,6 +46,7 @@ constructor( private val mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper, private val backActionInteractor: BackActionInteractor, private val powerInteractor: PowerInteractor, + private val keyguardMediaKeyInteractor: KeyguardMediaKeyInteractor, ) { fun dispatchKeyEvent(event: KeyEvent): Boolean { @@ -96,8 +98,15 @@ constructor( } fun interceptMediaKey(event: KeyEvent): Boolean { - return statusBarStateController.state == StatusBarState.KEYGUARD && - statusBarKeyguardViewManager.interceptMediaKey(event) + return when (statusBarStateController.state) { + StatusBarState.KEYGUARD -> + if (ComposeBouncerFlags.isEnabled) { + keyguardMediaKeyInteractor.processMediaKeyEvent(event) + } else { + statusBarKeyguardViewManager.interceptMediaKey(event) + } + else -> false + } } private fun dispatchMenuKeyEvent(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractor.kt new file mode 100644 index 000000000000..1404ef6a8fab --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractor.kt @@ -0,0 +1,103 @@ +/* + * 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.domain.interactor + +import android.media.AudioManager +import android.view.KeyEvent +import com.android.settingslib.volume.data.repository.AudioRepository +import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.telephony.domain.interactor.TelephonyInteractor +import javax.inject.Inject + +/** Handle media key events while on keyguard or bouncer. */ +@SysUISingleton +class KeyguardMediaKeyInteractor +@Inject +constructor( + private val telephonyInteractor: TelephonyInteractor, + private val audioRepository: AudioRepository, +) : ExclusiveActivatable() { + + /** + * Allows the media keys to work when the keyguard is showing. Forwards the relevant media keys + * to [AudioManager]. + * + * @param event The key event + * @return whether the event was consumed as a media key. + */ + fun processMediaKeyEvent(event: KeyEvent): Boolean { + if (ComposeBouncerFlags.isUnexpectedlyInLegacyMode()) { + return false + } + val keyCode = event.keyCode + if (event.action == KeyEvent.ACTION_DOWN) { + when (keyCode) { + KeyEvent.KEYCODE_MEDIA_PLAY, + KeyEvent.KEYCODE_MEDIA_PAUSE, + KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> { + /* Suppress PLAY/PAUSE toggle when phone is ringing or + * in-call to avoid music playback */ + // suppress key event + return telephonyInteractor.isInCall.value + } + + KeyEvent.KEYCODE_MUTE, + KeyEvent.KEYCODE_HEADSETHOOK, + KeyEvent.KEYCODE_MEDIA_STOP, + KeyEvent.KEYCODE_MEDIA_NEXT, + KeyEvent.KEYCODE_MEDIA_PREVIOUS, + KeyEvent.KEYCODE_MEDIA_REWIND, + KeyEvent.KEYCODE_MEDIA_RECORD, + KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, + KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK -> { + audioRepository.dispatchMediaKeyEvent(event) + return true + } + + KeyEvent.KEYCODE_VOLUME_UP, + KeyEvent.KEYCODE_VOLUME_DOWN, + KeyEvent.KEYCODE_VOLUME_MUTE -> return false + } + } else if (event.action == KeyEvent.ACTION_UP) { + when (keyCode) { + KeyEvent.KEYCODE_MUTE, + KeyEvent.KEYCODE_HEADSETHOOK, + KeyEvent.KEYCODE_MEDIA_PLAY, + KeyEvent.KEYCODE_MEDIA_PAUSE, + KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, + KeyEvent.KEYCODE_MEDIA_STOP, + KeyEvent.KEYCODE_MEDIA_NEXT, + KeyEvent.KEYCODE_MEDIA_PREVIOUS, + KeyEvent.KEYCODE_MEDIA_REWIND, + KeyEvent.KEYCODE_MEDIA_RECORD, + KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, + KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK -> { + audioRepository.dispatchMediaKeyEvent(event) + return true + } + } + } + return false + } + + override suspend fun onActivated(): Nothing { + // Collect to keep this flow hot for this interactor. + telephonyInteractor.isInCall.collect {} + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt index cf747c81769a..34173a9f44ea 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt @@ -29,6 +29,7 @@ import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.LightRevealEffect +import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf import com.android.systemui.util.kotlin.sample import dagger.Lazy import javax.inject.Inject @@ -96,16 +97,19 @@ constructor( /** Limit the max alpha for the scrim to allow for some transparency */ val maxAlpha: Flow<Float> = - transitionInteractor - .isInTransition( - edge = Edge.create(Scenes.Gone, KeyguardState.AOD), - edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD), + anyOf( + transitionInteractor.isInTransition( + edge = Edge.create(Scenes.Gone, KeyguardState.AOD), + edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD), + ), + transitionInteractor.isInTransition( + Edge.create(KeyguardState.OCCLUDED, KeyguardState.AOD) + ), ) .flatMapLatest { isInTransition -> - // During GONE->AOD transitions, the home screen and wallpaper are still visible - // until - // WM is told to hide them, which occurs at the end of the animation. Use an opaque - // scrim until this transition is complete + // During transitions like GONE->AOD, surfaces like the launcher may be visible + // until WM is told to hide them, which occurs at the end of the animation. Use an + // opaque scrim until this transition is complete. if (isInTransition) { flowOf(1f) } else { @@ -149,7 +153,6 @@ constructor( KeyguardState.DOZING -> false KeyguardState.AOD -> false KeyguardState.DREAMING -> true - KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true KeyguardState.GLANCEABLE_HUB -> true KeyguardState.ALTERNATE_BOUNCER -> true KeyguardState.PRIMARY_BOUNCER -> true diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt index 080ddfd18370..f0e79b8590a0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt @@ -41,12 +41,6 @@ enum class KeyguardState { */ DREAMING, /** - * A device state after the device times out, which can be from both LOCKSCREEN or GONE states. - * It is a special version of DREAMING state but not DOZING. The active dream will be windowless - * and hosted in the lockscreen. - */ - DREAMING_LOCKSCREEN_HOSTED, - /** * The device has entered a special low-power mode within SystemUI, also called the Always-on * Display (AOD). A minimal UI is presented to show critical information. If the device is in * low-power mode without a UI, then it is DOZING. @@ -125,7 +119,6 @@ enum class KeyguardState { OFF, DOZING, DREAMING, - DREAMING_LOCKSCREEN_HOSTED, AOD, ALTERNATE_BOUNCER, OCCLUDED, @@ -142,7 +135,6 @@ enum class KeyguardState { OFF, DOZING, DREAMING, - DREAMING_LOCKSCREEN_HOSTED, AOD, ALTERNATE_BOUNCER, OCCLUDED, @@ -166,7 +158,6 @@ enum class KeyguardState { OFF -> false DOZING -> false DREAMING -> false - DREAMING_LOCKSCREEN_HOSTED -> false GLANCEABLE_HUB -> true AOD -> false ALTERNATE_BOUNCER -> true diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt index 32757ce82c69..741cc02ffb6b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.binder import android.animation.ValueAnimator import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators.ALPHA_IN import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel import com.android.systemui.lifecycle.repeatWhenAttached @@ -43,14 +44,24 @@ object LightRevealScrimViewBinder { } } launch("$TAG#viewModel.maxAlpha") { - viewModel.maxAlpha.collect { alpha -> + var animator: ValueAnimator? = null + viewModel.maxAlpha.collect { (alpha, animate) -> if (alpha != revealScrim.alpha) { - ValueAnimator.ofFloat(revealScrim.alpha, alpha).apply { - duration = 400 - addUpdateListener { animation -> - revealScrim.alpha = animation.getAnimatedValue() as Float - } - start() + animator?.cancel() + if (!animate) { + revealScrim.alpha = alpha + } else { + animator = + ValueAnimator.ofFloat(revealScrim.alpha, alpha).apply { + startDelay = 333 + duration = 733 + interpolator = ALPHA_IN + addUpdateListener { animation -> + revealScrim.alpha = + animation.getAnimatedValue() as Float + } + start() + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt index 68244d842046..4c667c1c702d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt @@ -114,7 +114,6 @@ constructor( keyguardTransitionInteractor.currentKeyguardState.replayCache.last() ) { KeyguardState.GLANCEABLE_HUB, - KeyguardState.DREAMING_LOCKSCREEN_HOSTED, KeyguardState.GONE, KeyguardState.OCCLUDED, KeyguardState.OFF, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt index d3bb4f5d7508..f5e0c81ca9a2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt @@ -95,11 +95,10 @@ constructor( .shareIn(scope, SharingStarted.WhileSubscribed()) .onStart { emit(initialAlphaFromKeyguardState(transitionInteractor.getCurrentState())) } private val alphaMultiplierFromShadeExpansion: Flow<Float> = - combine( - showingAlternateBouncer, + combine(showingAlternateBouncer, shadeExpansion, qsProgress) { + showingAltBouncer, shadeExpansion, - qsProgress, - ) { showingAltBouncer, shadeExpansion, qsProgress -> + qsProgress -> val interpolatedQsProgress = (qsProgress * 2).coerceIn(0f, 1f) if (showingAltBouncer) { 1f @@ -113,13 +112,9 @@ constructor( combine( burnInInteractor.deviceEntryIconXOffset, burnInInteractor.deviceEntryIconYOffset, - burnInInteractor.udfpsProgress + burnInInteractor.udfpsProgress, ) { fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress -> - BurnInOffsets( - fullyDozingBurnInX, - fullyDozingBurnInY, - fullyDozingBurnInProgress, - ) + BurnInOffsets(fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress) } private val dozeAmount: Flow<Float> = transitionInteractor.transitionValue(KeyguardState.AOD) @@ -129,22 +124,15 @@ constructor( BurnInOffsets( intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.x), intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.y), - floatEvaluator.evaluate(dozeAmount, 0, burnInOffsets.progress) + floatEvaluator.evaluate(dozeAmount, 0, burnInOffsets.progress), ) } val deviceEntryViewAlpha: Flow<Float> = - combine( - transitionAlpha, - alphaMultiplierFromShadeExpansion, - ) { alpha, alphaMultiplier -> + combine(transitionAlpha, alphaMultiplierFromShadeExpansion) { alpha, alphaMultiplier -> alpha * alphaMultiplier } - .stateIn( - scope = scope, - started = SharingStarted.WhileSubscribed(), - initialValue = 0f, - ) + .stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initialValue = 0f) private fun initialAlphaFromKeyguardState(keyguardState: KeyguardState): Float { return when (keyguardState) { @@ -155,11 +143,10 @@ constructor( KeyguardState.GLANCEABLE_HUB, KeyguardState.GONE, KeyguardState.OCCLUDED, - KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - KeyguardState.UNDEFINED, -> 0f + KeyguardState.UNDEFINED -> 0f KeyguardState.AOD, KeyguardState.ALTERNATE_BOUNCER, - KeyguardState.LOCKSCREEN, -> 1f + KeyguardState.LOCKSCREEN -> 1f } } @@ -171,7 +158,7 @@ constructor( combine( transitionInteractor.startedKeyguardTransitionStep.sample( shadeInteractor.isAnyFullyExpanded, - ::Pair + ::Pair, ), animatedBurnInOffsets, nonAnimatedBurnInOffsets, @@ -228,10 +215,9 @@ constructor( } val iconType: Flow<DeviceEntryIconView.IconType> = - combine( - deviceEntryUdfpsInteractor.isListeningForUdfps, - isUnlocked, - ) { isListeningForUdfps, isUnlocked -> + combine(deviceEntryUdfpsInteractor.isListeningForUdfps, isUnlocked) { + isListeningForUdfps, + isUnlocked -> if (isListeningForUdfps) { if (isUnlocked) { // Don't show any UI until isUnlocked=false. This covers the case @@ -250,10 +236,7 @@ constructor( val isVisible: Flow<Boolean> = deviceEntryViewAlpha.map { it > 0f }.distinctUntilChanged() private val isInteractive: Flow<Boolean> = - combine( - iconType, - isUdfpsSupported, - ) { deviceEntryStatus, isUdfps -> + combine(iconType, isUdfpsSupported) { deviceEntryStatus, isUdfps -> when (deviceEntryStatus) { DeviceEntryIconView.IconType.LOCK -> isUdfps DeviceEntryIconView.IconType.UNLOCK -> true diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt index af6cd166479a..6d1aefe813c3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt @@ -21,6 +21,7 @@ import com.android.systemui.statusbar.LightRevealEffect import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map /** * Models UI state for the light reveal scrim, which is used during screen on and off animations to @@ -32,7 +33,16 @@ class LightRevealScrimViewModel constructor(private val interactor: LightRevealScrimInteractor) { val lightRevealEffect: Flow<LightRevealEffect> = interactor.lightRevealEffect val revealAmount: Flow<Float> = interactor.revealAmount - val maxAlpha: Flow<Float> = interactor.maxAlpha + + /** Max alpha for the scrim + whether to animate the change */ + val maxAlpha: Flow<Pair<Float, Boolean>> = + interactor.maxAlpha.map { alpha -> + Pair( + alpha, + // Darken immediately if going to be fully opaque + if (alpha == 1f) false else true, + ) + } fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) { interactor.setWallpaperSupportsAmbientMode(supportsAmbientMode) diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 6db91ac073ba..4071b135dfaf 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -221,7 +221,7 @@ constructor( { notificationScrimClippingParams.params.top }, // Only allow scrolling when we are fully expanded. That way, we don't intercept // swipes in lockscreen (when somehow QS is receiving touches). - { scrollState.canScrollForward && viewModel.isQsFullyExpanded }, + { (scrollState.canScrollForward && viewModel.isQsFullyExpanded) || isCustomizing }, ) frame.addView( composeView, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt index 71fa0ac30fb7..7b2593952599 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt @@ -77,25 +77,24 @@ fun LargeTileContent( colors: TileColors, squishiness: () -> Float, accessibilityUiState: AccessibilityUiState? = null, - toggleClickSupported: Boolean = false, iconShape: Shape = RoundedCornerShape(CommonTileDefaults.InactiveCornerRadius), - onClick: () -> Unit = {}, - onLongClick: () -> Unit = {}, + toggleClick: (() -> Unit)? = null, + onLongClick: (() -> Unit)? = null, ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = tileHorizontalArrangement(), ) { // Icon - val longPressLabel = longPressLabel() + val longPressLabel = longPressLabel().takeIf { onLongClick != null } Box( modifier = - Modifier.size(CommonTileDefaults.ToggleTargetSize).thenIf(toggleClickSupported) { + Modifier.size(CommonTileDefaults.ToggleTargetSize).thenIf(toggleClick != null) { Modifier.clip(iconShape) .verticalSquish(squishiness) .background(colors.iconBackground, { 1f }) .combinedClickable( - onClick = onClick, + onClick = toggleClick!!, onLongClick = onLongClick, onLongClickLabel = longPressLabel, ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt index d2ec958c17b7..b581c8bf953f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt @@ -202,14 +202,17 @@ fun DefaultEditTileGrid( topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) }, ) { innerPadding -> CompositionLocalProvider(LocalOverscrollConfiguration provides null) { + val scrollState = rememberScrollState() + LaunchedEffect(listState.dragInProgress) { + if (listState.dragInProgress) { + scrollState.animateScrollTo(0) + } + } + Column( verticalArrangement = spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)), - modifier = - modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(innerPadding), + modifier = modifier.fillMaxSize().verticalScroll(scrollState).padding(innerPadding), ) { AnimatedContent( targetState = listState.dragInProgress, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt index 52d526123430..5f28fe427707 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt @@ -160,19 +160,18 @@ fun Tile( ) } else { val iconShape = TileDefaults.animateIconShape(uiState.state) + val secondaryClick: (() -> Unit)? = + { tile.onSecondaryClick() }.takeIf { uiState.handlesSecondaryClick } + val longClick: (() -> Unit)? = + { tile.onLongClick(expandable) }.takeIf { uiState.handlesLongClick } LargeTileContent( label = uiState.label, secondaryLabel = uiState.secondaryLabel, icon = icon, colors = colors, iconShape = iconShape, - toggleClickSupported = state.handlesSecondaryClick, - onClick = { - if (state.handlesSecondaryClick) { - tile.onSecondaryClick() - } - }, - onLongClick = { tile.onLongClick(expandable) }, + toggleClick = secondaryClick, + onLongClick = longClick, accessibilityUiState = uiState.accessibilityUiState, squishiness = squishiness, ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt index aa420800be7b..56675e49d4e6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt @@ -33,6 +33,7 @@ data class TileUiState( val label: String, val secondaryLabel: String, val state: Int, + val handlesLongClick: Boolean, val handlesSecondaryClick: Boolean, val icon: Supplier<QSTile.Icon?>, val accessibilityUiState: AccessibilityUiState, @@ -86,6 +87,7 @@ fun QSTile.State.toUiState(resources: Resources): TileUiState { label = label?.toString() ?: "", secondaryLabel = secondaryLabel?.toString() ?: "", state = if (disabledByPolicy) Tile.STATE_UNAVAILABLE else state, + handlesLongClick = handlesLongClick, handlesSecondaryClick = handlesSecondaryClick, icon = icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null }, AccessibilityUiState( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt index fb406d47d7a6..1792ebd5245d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -152,7 +152,14 @@ constructor( recordingController.startCountdown( DELAY_MS, INTERVAL_MS, - pendingServiceIntent(getStartIntent(userContextProvider.userContext)), + pendingServiceIntent( + getStartIntent( + userContextProvider.userContext, + issueRecordingState.traceConfig, + issueRecordingState.recordScreen, + issueRecordingState.takeBugreport, + ) + ), pendingServiceIntent(getStopIntent(userContextProvider.userContext)), ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt index 0c8a3750f6fe..fceee5a3379e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent +import com.android.systemui.recordissue.IssueRecordingState import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC import com.android.systemui.screenrecord.RecordingController @@ -52,6 +53,7 @@ class IssueRecordingUserActionInteractor @Inject constructor( @Main private val mainCoroutineContext: CoroutineContext, + private val state: IssueRecordingState, private val keyguardDismissUtil: KeyguardDismissUtil, private val keyguardStateController: KeyguardStateController, private val dialogTransitionAnimator: DialogTransitionAnimator, @@ -104,8 +106,15 @@ constructor( recordingController.startCountdown( DELAY_MS, INTERVAL_MS, - pendingServiceIntent(getStartIntent(userContextProvider.userContext)), - pendingServiceIntent(getStopIntent(userContextProvider.userContext)) + pendingServiceIntent( + getStartIntent( + userContextProvider.userContext, + state.traceConfig, + state.recordScreen, + state.takeBugreport, + ) + ), + pendingServiceIntent(getStopIntent(userContextProvider.userContext)), ) private fun stopIssueRecordingService() = @@ -117,6 +126,6 @@ constructor( userContextProvider.userContext, RecordingService.REQUEST_CODE, action, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt index 3f875bcc288b..ad0ede44a58e 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt @@ -38,6 +38,8 @@ import com.android.systemui.screenrecord.RecordingService import com.android.systemui.screenrecord.RecordingServiceStrings import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.KeyguardDismissUtil +import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE +import com.android.traceur.TraceConfig import java.util.concurrent.Executor import javax.inject.Inject @@ -110,7 +112,13 @@ constructor( when (intent?.action) { ACTION_START -> { session.start() - if (!issueRecordingState.recordScreen) { + with(session) { + traceConfig = + intent.getParcelableExtra(INTENT_EXTRA_TRACE_TYPE, TraceConfig::class.java) + takeBugReport = intent.getBooleanExtra(EXTRA_BUG_REPORT, false) + start() + } + if (!intent.getBooleanExtra(EXTRA_SCREEN_RECORD, false)) { // If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action // will circumvent the RecordingService's screen recording start code. return super.onStartCommand(Intent(ACTION_SHOW_START_NOTIF), flags, startId) @@ -136,6 +144,8 @@ constructor( companion object { private const val TAG = "IssueRecordingService" private const val CHANNEL_ID = "issue_record" + const val EXTRA_SCREEN_RECORD = "extra_screenRecord" + const val EXTRA_BUG_REPORT = "extra_bugReport" /** * Get an intent to stop the issue recording service. @@ -153,8 +163,17 @@ constructor( * * @param context Context from the requesting activity */ - fun getStartIntent(context: Context): Intent = - Intent(context, IssueRecordingService::class.java).setAction(ACTION_START) + fun getStartIntent( + context: Context, + traceConfig: TraceConfig, + screenRecord: Boolean, + bugReport: Boolean, + ): Intent = + Intent(context, IssueRecordingService::class.java) + .setAction(ACTION_START) + .putExtra(INTENT_EXTRA_TRACE_TYPE, traceConfig) + .putExtra(EXTRA_SCREEN_RECORD, screenRecord) + .putExtra(EXTRA_BUG_REPORT, bugReport) } } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt index ad9b4fe164e8..4c01293d1cad 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt @@ -25,6 +25,7 @@ import android.provider.Settings import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.settings.UserContextProvider +import com.android.traceur.PresetTraceConfigs import java.util.concurrent.Executor private const val NOTIFY_SESSION_ENDED_SETTING = "should_notify_trace_session_ended" @@ -47,15 +48,17 @@ class IssueRecordingServiceSession( private val notificationManager: NotificationManager, private val userContextProvider: UserContextProvider, ) { + var takeBugReport = false + var traceConfig = PresetTraceConfigs.getDefaultConfig() fun start() { - bgExecutor.execute { traceurConnection.startTracing(issueRecordingState.traceConfig) } + bgExecutor.execute { traceurConnection.startTracing(traceConfig) } issueRecordingState.isRecording = true } fun stop(contentResolver: ContentResolver) { bgExecutor.execute { - if (issueRecordingState.traceConfig.longTrace) { + if (traceConfig.longTrace) { Settings.Global.putInt(contentResolver, NOTIFY_SESSION_ENDED_SETTING, DISABLED) } traceurConnection.stopTracing() @@ -71,7 +74,7 @@ class IssueRecordingServiceSession( UserHandle(userContextProvider.userContext.userId), ) - if (issueRecordingState.takeBugreport) { + if (takeBugReport) { iActivityManager.requestBugReportWithExtraAttachment(screenRecording) } else { traceurConnection.shareTraces(screenRecording) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt deleted file mode 100644 index f73d2041af95..000000000000 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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. - */ - -package com.android.systemui.screenshot - -import android.annotation.UserIdInt -import android.content.ComponentName -import android.graphics.Rect -import android.os.UserHandle -import android.view.Display - -/** - * Provides policy decision-making information to screenshot request handling. - */ -interface ScreenshotPolicy { - - /** @return true if the user is a managed profile (a.k.a. work profile) */ - suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean - - /** - * Requests information about the owner of display content which occupies a majority of the - * screenshot and/or has most recently been interacted with at the time the screenshot was - * requested. - * - * @param displayId the id of the display to inspect - * @return content info for the primary content on the display - */ - suspend fun findPrimaryContent(displayId: Int): DisplayContentInfo - - data class DisplayContentInfo( - val component: ComponentName, - val bounds: Rect, - val user: UserHandle, - val taskId: Int, - ) - - fun getDefaultDisplayId(): Int = Display.DEFAULT_DISPLAY -} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt deleted file mode 100644 index 21a73100da06..000000000000 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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. - */ - -package com.android.systemui.screenshot - -import android.annotation.UserIdInt -import android.app.ActivityTaskManager -import android.app.ActivityTaskManager.RootTaskInfo -import android.app.IActivityTaskManager -import android.app.WindowConfiguration -import android.app.WindowConfiguration.activityTypeToString -import android.app.WindowConfiguration.windowingModeToString -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.graphics.Rect -import android.os.Process -import android.os.RemoteException -import android.os.UserHandle -import android.os.UserManager -import android.util.Log -import com.android.internal.annotations.VisibleForTesting -import com.android.internal.infra.ServiceConnector -import com.android.systemui.SystemUIService -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.settings.DisplayTracker -import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo -import java.util.Arrays -import javax.inject.Inject -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.withContext - -@SysUISingleton -internal open class ScreenshotPolicyImpl @Inject constructor( - context: Context, - private val userMgr: UserManager, - private val atmService: IActivityTaskManager, - @Background val bgDispatcher: CoroutineDispatcher, - private val displayTracker: DisplayTracker -) : ScreenshotPolicy { - - private val proxyConnector: ServiceConnector<IScreenshotProxy> = - ServiceConnector.Impl( - context, - Intent(context, ScreenshotProxyService::class.java), - Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE, - context.userId, - IScreenshotProxy.Stub::asInterface - ) - - override fun getDefaultDisplayId(): Int { - return displayTracker.defaultDisplayId - } - - override suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean { - val managed = withContext(bgDispatcher) { userMgr.isManagedProfile(userId) } - Log.d(TAG, "isManagedProfile: $managed") - return managed - } - - private fun nonPipVisibleTask(info: RootTaskInfo): Boolean { - if (DEBUG) { - debugLogRootTaskInfo(info) - } - return info.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED && - info.isVisible && - info.isRunning && - info.numActivities > 0 && - info.topActivity != null && - info.childTaskIds.isNotEmpty() - } - - /** - * Uses RootTaskInfo from ActivityTaskManager to guess at the primary focused task within a - * display. If no task is visible or the top task is covered by a system window, the info - * reported will reference a SystemUI component instead. - */ - override suspend fun findPrimaryContent(displayId: Int): DisplayContentInfo { - // Determine if the notification shade is expanded. If so, task windows are not - // visible behind it, so the screenshot should instead be associated with SystemUI. - if (isNotificationShadeExpanded()) { - return systemUiContent - } - - val taskInfoList = getAllRootTaskInfosOnDisplay(displayId) - - // If no visible task is located, then report SystemUI as the foreground content - val target = taskInfoList.firstOrNull(::nonPipVisibleTask) ?: return systemUiContent - return target.toDisplayContentInfo() - } - - private fun debugLogRootTaskInfo(info: RootTaskInfo) { - Log.d(TAG, "RootTaskInfo={" + - "taskId=${info.taskId} " + - "parentTaskId=${info.parentTaskId} " + - "position=${info.position} " + - "positionInParent=${info.positionInParent} " + - "isVisible=${info.isVisible()} " + - "visible=${info.visible} " + - "isFocused=${info.isFocused} " + - "isSleeping=${info.isSleeping} " + - "isRunning=${info.isRunning} " + - "windowMode=${windowingModeToString(info.windowingMode)} " + - "activityType=${activityTypeToString(info.activityType)} " + - "topActivity=${info.topActivity} " + - "topActivityInfo=${info.topActivityInfo} " + - "numActivities=${info.numActivities} " + - "childTaskIds=${Arrays.toString(info.childTaskIds)} " + - "childUserIds=${Arrays.toString(info.childTaskUserIds)} " + - "childTaskBounds=${Arrays.toString(info.childTaskBounds)} " + - "childTaskNames=${Arrays.toString(info.childTaskNames)}" + - "}" - ) - - for (j in 0 until info.childTaskIds.size) { - Log.d(TAG, " *** [$j] ******") - Log.d(TAG, " *** childTaskIds[$j]: ${info.childTaskIds[j]}") - Log.d(TAG, " *** childTaskUserIds[$j]: ${info.childTaskUserIds[j]}") - Log.d(TAG, " *** childTaskBounds[$j]: ${info.childTaskBounds[j]}") - Log.d(TAG, " *** childTaskNames[$j]: ${info.childTaskNames[j]}") - } - } - - @VisibleForTesting - open suspend fun getAllRootTaskInfosOnDisplay(displayId: Int): List<RootTaskInfo> = - withContext(bgDispatcher) { - try { - atmService.getAllRootTaskInfosOnDisplay(displayId) - } catch (e: RemoteException) { - Log.e(TAG, "getAllRootTaskInfosOnDisplay", e) - listOf() - } - } - - @VisibleForTesting - open suspend fun isNotificationShadeExpanded(): Boolean = suspendCoroutine { k -> - proxyConnector - .postForResult { it.isNotificationShadeExpanded } - .whenComplete { expanded, error -> - if (error != null) { - Log.e(TAG, "isNotificationShadeExpanded", error) - } - k.resume(expanded ?: false) - } - } - - @VisibleForTesting - internal val systemUiContent = - DisplayContentInfo( - ComponentName(context, SystemUIService::class.java), - Rect(), - Process.myUserHandle(), - ActivityTaskManager.INVALID_TASK_ID - ) -} - -private const val TAG: String = "ScreenshotPolicyImpl" -private const val DEBUG: Boolean = false - -@VisibleForTesting -internal fun RootTaskInfo.toDisplayContentInfo(): DisplayContentInfo { - val topActivity: ComponentName = topActivity ?: error("should not be null") - val topChildTask = childTaskIds.size - 1 - val childTaskId = childTaskIds[topChildTask] - val childTaskUserId = childTaskUserIds[topChildTask] - val childTaskBounds = childTaskBounds[topChildTask] - - return DisplayContentInfo( - topActivity, - childTaskBounds, - UserHandle.of(childTaskUserId), - childTaskId) -} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java index 254dde45148c..90695fa00ceb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java @@ -27,8 +27,6 @@ import com.android.systemui.screenshot.ImageCaptureImpl; import com.android.systemui.screenshot.InteractiveScreenshotHandler; import com.android.systemui.screenshot.LegacyScreenshotController; import com.android.systemui.screenshot.ScreenshotController; -import com.android.systemui.screenshot.ScreenshotPolicy; -import com.android.systemui.screenshot.ScreenshotPolicyImpl; import com.android.systemui.screenshot.ScreenshotSoundController; import com.android.systemui.screenshot.ScreenshotSoundControllerImpl; import com.android.systemui.screenshot.ScreenshotSoundProvider; @@ -66,9 +64,6 @@ public abstract class ScreenshotModule { TakeScreenshotExecutorImpl impl); @Binds - abstract ScreenshotPolicy bindScreenshotPolicyImpl(ScreenshotPolicyImpl impl); - - @Binds abstract ImageCapture bindImageCaptureImpl(ImageCaptureImpl capture); @Binds diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt index dae851252271..258befe61bfe 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt @@ -18,15 +18,19 @@ package com.android.systemui.settings import android.content.Context -/** - * Implemented by [UserTrackerImpl]. - */ +/** Implemented by [UserTrackerImpl]. */ interface UserContextProvider { + /** + * provides system context, not current user context. + * + * To get current user context use [createCurrentUserContext] passing [userContext] as context + */ val userContext: Context /** * Creates the {@code context} with the current user. + * * @see Context#createContextAsUser(UserHandle, int) */ fun createCurrentUserContext(context: Context): Context -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java index 75165cb5610e..8703f68f39aa 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java @@ -40,6 +40,8 @@ import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.util.ViewController; import com.android.systemui.util.time.SystemClock; +import com.google.android.msdl.domain.MSDLPlayer; + import javax.inject.Inject; /** @@ -283,12 +285,14 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV private final VibratorHelper mVibratorHelper; private final SystemClock mSystemClock; private final ActivityStarter mActivityStarter; + private final MSDLPlayer mMSDLPlayer; @Inject public Factory( FalsingManager falsingManager, UiEventLogger uiEventLogger, VibratorHelper vibratorHelper, + MSDLPlayer msdlPlayer, SystemClock clock, ActivityStarter activityStarter ) { @@ -297,6 +301,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV mVibratorHelper = vibratorHelper; mSystemClock = clock; mActivityStarter = activityStarter; + mMSDLPlayer = msdlPlayer; } /** @@ -314,6 +319,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV .inflate(layout, viewRoot, false); SeekbarHapticPlugin plugin = new SeekbarHapticPlugin( mVibratorHelper, + mMSDLPlayer, mSystemClock); HapticSliderViewBinder.bind(viewRoot, plugin); return new BrightnessSliderController( diff --git a/packages/SystemUI/src/com/android/systemui/shade/LockscreenHostedDreamGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/LockscreenHostedDreamGestureListener.kt deleted file mode 100644 index 45fc68a793bd..000000000000 --- a/packages/SystemUI/src/com/android/systemui/shade/LockscreenHostedDreamGestureListener.kt +++ /dev/null @@ -1,72 +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.shade - -import android.os.PowerManager -import android.view.GestureDetector -import android.view.MotionEvent -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.data.repository.KeyguardRepository -import com.android.systemui.plugins.FalsingManager -import com.android.systemui.plugins.FalsingManager.LOW_PENALTY -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.power.domain.interactor.PowerInteractor -import com.android.systemui.statusbar.StatusBarState -import javax.inject.Inject - -/** - * This gestureListener will wake up by tap when the device is dreaming but not dozing, and the - * selected screensaver is hosted in lockscreen. Tap is gated by the falsing manager. - * - * Touches go through the [NotificationShadeWindowViewController]. - */ -@SysUISingleton -class LockscreenHostedDreamGestureListener -@Inject -constructor( - private val falsingManager: FalsingManager, - private val powerInteractor: PowerInteractor, - private val statusBarStateController: StatusBarStateController, - private val primaryBouncerInteractor: PrimaryBouncerInteractor, - private val keyguardRepository: KeyguardRepository, - private val shadeLogger: ShadeLogger, -) : GestureDetector.SimpleOnGestureListener() { - private val TAG = this::class.simpleName - - override fun onSingleTapUp(e: MotionEvent): Boolean { - if (shouldHandleMotionEvent()) { - if (!falsingManager.isFalseTap(LOW_PENALTY)) { - shadeLogger.d("$TAG#onSingleTapUp tap handled, requesting wakeUpIfDreaming") - powerInteractor.wakeUpIfDreaming( - "DREAMING_SINGLE_TAP", - PowerManager.WAKE_REASON_TAP - ) - } else { - shadeLogger.d("$TAG#onSingleTapUp false tap ignored") - } - return true - } - return false - } - - private fun shouldHandleMotionEvent(): Boolean { - return keyguardRepository.isActiveDreamLockscreenHosted.value && - statusBarStateController.state == StatusBarState.KEYGUARD && - !primaryBouncerInteractor.isBouncerShowing() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt new file mode 100644 index 000000000000..111d335d0d0f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt @@ -0,0 +1,33 @@ +/* + * 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.shade + +import javax.inject.Qualifier + +/** + * Qualifies classes that provide display-specific info for shade window components. + * + * The Shade window can be moved between displays with different characteristics (e.g., density, + * size). This annotation ensures that components within the shade window use the correct context + * and resources for the display they are currently on. + * + * Classes annotated with `@ShadeDisplayAware` (e.g., 'Context`, `Resources`, `LayoutInflater`, + * `ConfigurationController`) will be dynamically updated to reflect the current display's + * configuration. This ensures consistent rendering even when the shade window is moved to an + * external display. + */ +@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class ShadeDisplayAware diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index f99d8f140670..520cbf9d80d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -214,7 +214,7 @@ public class KeyguardIndicationController { private boolean mEnableBatteryDefender; private boolean mIncompatibleCharger; private int mChargingWattage; - private int mBatteryLevel; + private int mBatteryLevel = -1; private boolean mBatteryPresent = true; protected long mChargingTimeRemaining; private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn; @@ -1032,12 +1032,16 @@ public class KeyguardIndicationController { } else if (!TextUtils.isEmpty(mTransientIndication)) { newIndication = mTransientIndication; } else if (!mBatteryPresent) { - // If there is no battery detected, hide the indication and bail + // If there is no battery detected, hide the indication area and bail mIndicationArea.setVisibility(GONE); return; } else if (!TextUtils.isEmpty(mAlignmentIndication)) { useMisalignmentColor = true; newIndication = mAlignmentIndication; + } else if (mBatteryLevel == -1) { + // If the battery level is not initialized, hide the indication area + mIndicationArea.setVisibility(GONE); + return; } else if (mPowerPluggedIn || mEnableBatteryDefender) { newIndication = computePowerIndication(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index 2930de2fd9ee..0eef8d63c2d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -44,8 +44,12 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN import com.android.systemui.util.leak.RotationUtils.Rotation +import dagger.Module +import dagger.Provides +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import java.util.concurrent.Executor -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -63,26 +67,57 @@ import kotlinx.coroutines.launch * NOTE: any operation that modifies views directly must run on the provided executor, because these * views are owned by ScreenDecorations and it runs in its own thread */ -@SysUISingleton -open class PrivacyDotViewController -@Inject +interface PrivacyDotViewController { + + // Only can be modified on @UiThread + var currentViewState: ViewState + + var showingListener: ShowingListener? + + fun setUiExecutor(e: DelayableExecutor) + + fun getUiExecutor(): DelayableExecutor? + + @UiThread fun setNewRotation(rot: Int) + + @UiThread fun hideDotView(dot: View, animate: Boolean) + + @UiThread fun showDotView(dot: View, animate: Boolean) + + // Update the gravity and margins of the privacy views + @UiThread fun updateRotations(rotation: Int, paddingTop: Int) + + @UiThread fun setCornerSizes(state: ViewState) + + fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) + + @UiThread fun updateDotView(state: ViewState) + + interface ShowingListener { + fun onPrivacyDotShown(v: View?) + + fun onPrivacyDotHidden(v: View?) + } +} + +open class PrivacyDotViewControllerImpl +@AssistedInject constructor( @Main private val mainExecutor: Executor, - @Application scope: CoroutineScope, + @Assisted scope: CoroutineScope, private val stateController: StatusBarStateController, - private val configurationController: ConfigurationController, - private val contentInsetsProvider: StatusBarContentInsetsProvider, + @Assisted private val configurationController: ConfigurationController, + @Assisted private val contentInsetsProvider: StatusBarContentInsetsProvider, private val animationScheduler: SystemStatusAnimationScheduler, - shadeInteractor: ShadeInteractor? -) { + shadeInteractor: ShadeInteractor?, +) : PrivacyDotViewController { private lateinit var tl: View private lateinit var tr: View private lateinit var bl: View private lateinit var br: View // Only can be modified on @UiThread - var currentViewState: ViewState = ViewState() - get() = field + override var currentViewState: ViewState = ViewState() @GuardedBy("lock") private var nextViewState: ViewState = currentViewState.copy() @@ -100,11 +135,7 @@ constructor( private val views: Sequence<View> get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl) - var showingListener: ShowingListener? = null - set(value) { - field = value - } - get() = field + override var showingListener: PrivacyDotViewController.ShowingListener? = null init { contentInsetsProvider.addCallback( @@ -153,16 +184,16 @@ constructor( } } - fun setUiExecutor(e: DelayableExecutor) { + override fun setUiExecutor(e: DelayableExecutor) { uiExecutor = e } - fun getUiExecutor(): DelayableExecutor? { + override fun getUiExecutor(): DelayableExecutor? { return uiExecutor } @UiThread - fun setNewRotation(rot: Int) { + override fun setNewRotation(rot: Int) { dlog("updateRotation: $rot") val isRtl: Boolean @@ -187,13 +218,13 @@ constructor( rotation = rot, paddingTop = paddingTop, designatedCorner = newCorner, - cornerIndex = index + cornerIndex = index, ) } } @UiThread - fun hideDotView(dot: View, animate: Boolean) { + override fun hideDotView(dot: View, animate: Boolean) { dot.clearAnimation() if (animate) { dot.animate() @@ -212,7 +243,7 @@ constructor( } @UiThread - fun showDotView(dot: View, animate: Boolean) { + override fun showDotView(dot: View, animate: Boolean) { dot.clearAnimation() if (animate) { dot.visibility = View.VISIBLE @@ -229,9 +260,8 @@ constructor( showingListener?.onPrivacyDotShown(dot) } - // Update the gravity and margins of the privacy views @UiThread - open fun updateRotations(rotation: Int, paddingTop: Int) { + override fun updateRotations(rotation: Int, paddingTop: Int) { // To keep a view in the corner, its gravity is always the description of its current corner // Therefore, just figure out which view is in which corner. This turns out to be something // like (myCorner - rot) mod 4, where topLeft = 0, topRight = 1, etc. and portrait = 0, and @@ -262,7 +292,7 @@ constructor( } @UiThread - open fun setCornerSizes(state: ViewState) { + override fun setCornerSizes(state: ViewState) { // StatusBarContentInsetsProvider can tell us the location of the privacy indicator dot // in every rotation. The only thing we need to check is rtl val rtl = state.layoutRtl @@ -415,7 +445,7 @@ constructor( } } - fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) { + override fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) { if ( this::tl.isInitialized && this::tr.isInitialized && @@ -457,7 +487,7 @@ constructor( landscapeRect = right, upsideDownRect = bottom, paddingTop = paddingTop, - layoutRtl = rtl + layoutRtl = rtl, ) } } @@ -533,7 +563,7 @@ constructor( } @UiThread - open fun updateDotView(state: ViewState) { + override fun updateDotView(state: ViewState) { val shouldShow = state.shouldShowDot() if (shouldShow != currentViewState.shouldShowDot()) { if (shouldShow && state.designatedCorner != null) { @@ -553,7 +583,7 @@ constructor( nextViewState = nextViewState.copy( systemPrivacyEventIsActive = true, - contentDescription = contentDescr + contentDescription = contentDescr, ) } @@ -595,15 +625,18 @@ constructor( seascapeRect = rects[0], portraitRect = rects[1], landscapeRect = rects[2], - upsideDownRect = rects[3] + upsideDownRect = rects[3], ) } } - interface ShowingListener { - fun onPrivacyDotShown(v: View?) - - fun onPrivacyDotHidden(v: View?) + @AssistedFactory + interface Factory { + fun create( + scope: CoroutineScope, + configurationController: ConfigurationController, + contentInsetsProvider: StatusBarContentInsetsProvider, + ): PrivacyDotViewControllerImpl } } @@ -662,7 +695,7 @@ data class ViewState( val paddingTop: Int = 0, val cornerIndex: Int = -1, val designatedCorner: View? = null, - val contentDescription: String? = null + val contentDescription: String? = null, ) { fun shouldShowDot(): Boolean { return systemPrivacyEventIsActive && !shadeExpanded && !qsExpanded @@ -687,3 +720,18 @@ data class ViewState( } } } + +@Module +object PrivacyDotViewControllerModule { + + @Provides + @SysUISingleton + fun controller( + factory: PrivacyDotViewControllerImpl.Factory, + @Application scope: CoroutineScope, + configurationController: ConfigurationController, + contentInsetsProvider: StatusBarContentInsetsProvider, + ): PrivacyDotViewController { + return factory.create(scope, configurationController, contentInsetsProvider) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt index 231a0b0b21cb..9fe4a5499ceb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt @@ -32,13 +32,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow interface NotificationActivityStarter { /** Called when the user clicks on the notification bubble icon. */ - fun onNotificationBubbleIconClicked(entry: NotificationEntry?) + fun onNotificationBubbleIconClicked(entry: NotificationEntry) /** Called when the user clicks on the surface of a notification. */ - fun onNotificationClicked(entry: NotificationEntry?, row: ExpandableNotificationRow?) + fun onNotificationClicked(entry: NotificationEntry, row: ExpandableNotificationRow) /** Called when the user clicks on a button in the notification guts which fires an intent. */ - fun startNotificationGutsIntent(intent: Intent?, appUid: Int, row: ExpandableNotificationRow?) + fun startNotificationGutsIntent(intent: Intent, appUid: Int, row: ExpandableNotificationRow) /** * Called when the user clicks "Manage" or "History" in the Shade. Prefer using @@ -56,7 +56,7 @@ interface NotificationActivityStarter { fun startSettingsIntent(view: View, intentInfo: SettingsIntent) /** Called when the user succeed to drop notification to proper target view. */ - fun onDragSuccess(entry: NotificationEntry?) + fun onDragSuccess(entry: NotificationEntry) val isCollapsingToShowActivityOverLockscreen: Boolean get() = false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt index 2ee1dffd14f3..958001625a07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt @@ -35,6 +35,9 @@ import java.util.concurrent.atomic.AtomicInteger * This cache is safe for multithreaded usage, and is recommended for objects that take a while to * resolve (such as drawables, or things that require binder calls). As such, [getOrFetch] is * recommended to be run on a background thread, while [purge] can be done from any thread. + * + * Important: This cache does NOT have a maximum size, cleaning it up (via [purge]) is the + * responsibility of the caller, to avoid keeping things in memory unnecessarily. */ @SuppressLint("DumpableNotRegistered") // this will be dumped by container classes class NotifCollectionCache<V>( @@ -151,7 +154,7 @@ class NotifCollectionCache<V>( * purge((c)); // deletes a from the cache and marks b for deletion, etc. * ``` */ - fun purge(wantedKeys: List<String>) { + fun purge(wantedKeys: Collection<String>) { for ((key, entry) in cache) { if (key in wantedKeys) { entry.resetLives() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java index 9b075a650b48..f75163d2662b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -27,6 +27,7 @@ import android.os.Trace; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -49,13 +50,18 @@ import com.android.systemui.statusbar.notification.collection.render.NotifViewBa import com.android.systemui.statusbar.notification.collection.render.NotifViewController; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager.NotifInflationErrorListener; +import com.android.systemui.statusbar.notification.row.icon.AppIconProvider; +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import javax.inject.Inject; @@ -104,6 +110,8 @@ public class PreparationCoordinator implements Coordinator { /** How long we can delay a group while waiting for all children to inflate */ private final long mMaxGroupInflationDelay; private final BindEventManagerImpl mBindEventManager; + private final AppIconProvider mAppIconProvider; + private final NotificationIconStyleProvider mNotificationIconStyleProvider; @Inject public PreparationCoordinator( @@ -113,7 +121,9 @@ public class PreparationCoordinator implements Coordinator { NotifViewBarn viewBarn, NotifUiAdjustmentProvider adjustmentProvider, IStatusBarService service, - BindEventManagerImpl bindEventManager) { + BindEventManagerImpl bindEventManager, + AppIconProvider appIconProvider, + NotificationIconStyleProvider notificationIconStyleProvider) { this( logger, notifInflater, @@ -122,6 +132,8 @@ public class PreparationCoordinator implements Coordinator { adjustmentProvider, service, bindEventManager, + appIconProvider, + notificationIconStyleProvider, CHILD_BIND_CUTOFF, MAX_GROUP_INFLATION_DELAY); } @@ -135,6 +147,8 @@ public class PreparationCoordinator implements Coordinator { NotifUiAdjustmentProvider adjustmentProvider, IStatusBarService service, BindEventManagerImpl bindEventManager, + AppIconProvider appIconProvider, + NotificationIconStyleProvider notificationIconStyleProvider, int childBindCutoff, long maxGroupInflationDelay) { mLogger = logger; @@ -146,6 +160,8 @@ public class PreparationCoordinator implements Coordinator { mChildBindCutoff = childBindCutoff; mMaxGroupInflationDelay = maxGroupInflationDelay; mBindEventManager = bindEventManager; + mAppIconProvider = appIconProvider; + mNotificationIconStyleProvider = notificationIconStyleProvider; } @Override @@ -155,6 +171,9 @@ public class PreparationCoordinator implements Coordinator { () -> mNotifInflatingFilter.invalidateList("adjustmentProviderChanged")); pipeline.addCollectionListener(mNotifCollectionListener); + if (android.app.Flags.notificationsRedesignAppIcons()) { + pipeline.addOnBeforeTransformGroupsListener(this::purgeCaches); + } // Inflate after grouping/sorting since that affects what views to inflate. pipeline.addOnBeforeFinalizeFilterListener(this::inflateAllRequiredViews); pipeline.addFinalizeFilter(mNotifInflationErrorFilter); @@ -260,6 +279,29 @@ public class PreparationCoordinator implements Coordinator { } }; + private void purgeCaches(Collection<ListEntry> entries) { + Set<String> wantedPackages = getPackages(entries); + mAppIconProvider.purgeCache(wantedPackages); + mNotificationIconStyleProvider.purgeCache(wantedPackages); + } + + /** + * Get all app packages present in {@param entries}. + */ + private static @NonNull Set<String> getPackages(Collection<ListEntry> entries) { + Set<String> packages = new HashSet<>(); + for (ListEntry entry : entries) { + NotificationEntry notificationEntry = entry.getRepresentativeEntry(); + if (notificationEntry == null) { + Log.wtf(TAG, "notification entry " + entry.getKey() + + " has no representative entry"); + continue; + } + packages.add(notificationEntry.getSbn().getPackageName()); + } + return packages; + } + private void inflateAllRequiredViews(List<ListEntry> entries) { for (int i = 0, size = entries.size(); i < size; i++) { ListEntry entry = entries.get(i); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/EnsureEnrViewsVisibility.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/EnsureEnrViewsVisibility.kt new file mode 100644 index 000000000000..aa63f4ddbd45 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/EnsureEnrViewsVisibility.kt @@ -0,0 +1,61 @@ +/* + * 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.statusbar.notification.row + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the ensure enr views visibility flag state. */ +@Suppress("NOTHING_TO_INLINE") +object EnsureEnrViewsVisibility { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_ENSURE_ENR_VIEWS_VISIBILITY + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the refactor enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.ensureEnrViewsVisibility() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is not enabled to ensure that the refactor author catches issues in testing. + * Caution!! Using this check incorrectly will cause crashes in nextfood builds! + */ + @JvmStatic + inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 933f79341b27..a4c43a1b65c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -2507,6 +2507,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private void updateChildrenVisibility() { + if (EnsureEnrViewsVisibility.isEnabled()) { + mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE); + } + boolean hideContentWhileLaunching = mExpandAnimationRunning && mGuts != null && mGuts.isExposed(); mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren @@ -3073,7 +3077,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } else { mLogger.logSkipResetAllContentAlphas(getEntry()); } - mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE); + + if (!EnsureEnrViewsVisibility.isEnabled()) { + // mPublicLayout.setVisibility moved to updateChildrenVisibility when the flag is on + // in order to ensure public and private views are not visible + // together at the same time. + mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE); + } updateChildrenVisibility(); } else { animateShowingPublic(delay, duration, mShowingPublic); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt index 24b5cf1aa33b..0ddf9f7270df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row.icon +import android.annotation.WorkerThread import android.app.ActivityManager import android.app.Flags import android.content.Context @@ -27,20 +28,45 @@ import android.graphics.drawable.Drawable import android.util.Log import com.android.internal.R import com.android.launcher3.icons.BaseIconFactory +import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.notification.collection.NotifCollectionCache +import com.android.systemui.util.asIndenting +import com.android.systemui.util.withIncreasedIndent import dagger.Module import dagger.Provides +import java.io.PrintWriter import javax.inject.Inject import javax.inject.Provider /** A provider used to cache and fetch app icons used by notifications. */ interface AppIconProvider { + /** + * Loads the icon corresponding to [packageName] into cache, or fetches it from there if already + * present. This should only be called from the background. + */ @Throws(NameNotFoundException::class) + @WorkerThread fun getOrFetchAppIcon(packageName: String, context: Context): Drawable + + /** + * Mark all the entries in the cache that are NOT in [wantedPackages] to be cleared. If they're + * still not needed on the next call of this method (made after a timeout of 1s, in case they + * happen more frequently than that), they will be purged. This can be done from any thread. + */ + fun purgeCache(wantedPackages: Collection<String>) } @SysUISingleton -class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) : AppIconProvider { +class AppIconProviderImpl +@Inject +constructor(private val sysuiContext: Context, dumpManager: DumpManager) : + AppIconProvider, Dumpable { + init { + dumpManager.registerNormalDumpable(TAG, this) + } + private val iconFactory: BaseIconFactory get() { val isLowRam = ActivityManager.isLowRamDeviceStatic() @@ -53,13 +79,42 @@ class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) return BaseIconFactory(sysuiContext, res.configuration.densityDpi, iconSize) } + private val cache = NotifCollectionCache<Drawable>() + override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { + return cache.getOrFetch(packageName) { fetchAppIcon(packageName, context) } + } + + @WorkerThread + private fun fetchAppIcon(packageName: String, context: Context): BitmapDrawable { val icon = context.packageManager.getApplicationIcon(packageName) return BitmapDrawable( context.resources, iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_HARDWARE), ) } + + override fun purgeCache(wantedPackages: Collection<String>) { + cache.purge(wantedPackages) + } + + override fun dump(pwOrig: PrintWriter, args: Array<out String>) { + val pw = pwOrig.asIndenting() + + pw.println("cache information:") + pw.withIncreasedIndent { cache.dump(pw, args) } + + val iconFactory = iconFactory + pw.println("icon factory information:") + pw.withIncreasedIndent { + pw.println("fullResIconDpi = ${iconFactory.fullResIconDpi}") + pw.println("iconSize = ${iconFactory.iconBitmapSize}") + } + } + + companion object { + const val TAG = "AppIconProviderImpl" + } } class NoOpIconProvider : AppIconProvider { @@ -71,6 +126,10 @@ class NoOpIconProvider : AppIconProvider { Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.") return ColorDrawable(Color.WHITE) } + + override fun purgeCache(wantedPackages: Collection<String>) { + Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.") + } } @Module diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt index 165c1a7803a9..35e38c2c0c14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt @@ -22,9 +22,15 @@ import android.content.Context import android.content.pm.ApplicationInfo import android.service.notification.StatusBarNotification import android.util.Log +import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.notification.collection.NotifCollectionCache +import com.android.systemui.util.asIndenting +import com.android.systemui.util.withIncreasedIndent import dagger.Module import dagger.Provides +import java.io.PrintWriter import javax.inject.Inject import javax.inject.Provider @@ -33,15 +39,35 @@ import javax.inject.Provider * notifications. */ interface NotificationIconStyleProvider { + /** + * Determines whether the [notification] should display the app icon instead of the small icon. + * This can result in a binder call, and therefore should only be called from the background. + */ @WorkerThread fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean + + /** + * Mark all the entries in the cache that are NOT in [wantedPackages] to be cleared. If they're + * still not needed on the next call of this method (made after a timeout of 1s, in case they + * happen more frequently than that), they will be purged. This can be done from any thread. + */ + fun purgeCache(wantedPackages: Collection<String>) } @SysUISingleton -class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIconStyleProvider { +class NotificationIconStyleProviderImpl @Inject constructor(dumpManager: DumpManager) : + NotificationIconStyleProvider, Dumpable { + init { + dumpManager.registerNormalDumpable(TAG, this) + } + + private val cache = NotifCollectionCache<Boolean>() + override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean { val packageContext = notification.getPackageContext(context) - return !belongsToHeadlessSystemApp(packageContext) + return cache.getOrFetch(notification.packageName) { + !belongsToHeadlessSystemApp(packageContext) + } } @WorkerThread @@ -62,6 +88,20 @@ class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIcon return false } } + + override fun purgeCache(wantedPackages: Collection<String>) { + cache.purge(wantedPackages) + } + + override fun dump(pwOrig: PrintWriter, args: Array<out String>) { + val pw = pwOrig.asIndenting() + pw.println("cache information:") + pw.withIncreasedIndent { cache.dump(pw, args) } + } + + companion object { + const val TAG = "NotificationIconStyleProviderImpl" + } } class NoOpIconStyleProvider : NotificationIconStyleProvider { @@ -73,6 +113,10 @@ class NoOpIconStyleProvider : NotificationIconStyleProvider { Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.") return true } + + override fun purgeCache(wantedPackages: Collection<String>) { + Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.") + } } @Module diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt index a8c823c35213..858cac111525 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt @@ -29,9 +29,8 @@ import dagger.assisted.AssistedInject class ConfigurationControllerImpl @AssistedInject -constructor( - @Assisted private val context: Context, -) : ConfigurationController, StatusBarConfigurationController { +constructor(@Assisted private val context: Context) : + ConfigurationController, StatusBarConfigurationController { private val listeners: MutableList<ConfigurationListener> = ArrayList() private val lastConfig = Configuration() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt new file mode 100644 index 000000000000..3fd46fc484a9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt @@ -0,0 +1,31 @@ +/* + * 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.statusbar.phone + +import android.content.res.Configuration + +/** + * Used to forward a configuration change to other components. + * + * This is commonly used to propagate configs to [ConfigurationController]. Note that there could be + * different configuration forwarder, for example each display, window or group of classes (e.g. + * shade window classes). + */ +interface ConfigurationForwarder { + /** Should be called when a new configuration is received. */ + fun onConfigurationChanged(newConfiguration: Configuration) +} 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 74c6e72d3400..f7fea7b0d334 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1535,6 +1535,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public boolean interceptMediaKey(KeyEvent event) { + ComposeBouncerFlags.assertInLegacyMode(); return mPrimaryBouncerView.getDelegate() != null && mPrimaryBouncerView.getDelegate().interceptMediaKey(event); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 93db2db918b0..af98311c937f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -20,7 +20,6 @@ import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED import static android.service.notification.NotificationListenerService.REASON_CLICK; import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions; -import static com.android.systemui.util.kotlin.NullabilityKt.expectNotNull; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -231,8 +230,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit * @param entry notification that bubble icon was clicked */ @Override - public void onNotificationBubbleIconClicked(NotificationEntry entry) { - expectNotNull(TAG, "entry", entry); + public void onNotificationBubbleIconClicked(@NonNull NotificationEntry entry) { Runnable action = () -> { mBubblesManagerOptional.ifPresent(bubblesManager -> bubblesManager.onUserChangedBubble(entry, !entry.isBubble())); @@ -258,9 +256,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit * @param row row for that notification */ @Override - public void onNotificationClicked(NotificationEntry entry, ExpandableNotificationRow row) { - expectNotNull(TAG, "entry", entry); - expectNotNull(TAG, "row", row); + public void onNotificationClicked(@NonNull NotificationEntry entry, + @NonNull ExpandableNotificationRow row) { mLogger.logStartingActivityFromClick(entry, row.isHeadsUpState(), mKeyguardStateController.isVisible(), mNotificationShadeWindowController.getPanelExpanded()); @@ -442,8 +439,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit * @param entry notification entry that is dropped. */ @Override - public void onDragSuccess(NotificationEntry entry) { - expectNotNull(TAG, "entry", entry); + public void onDragSuccess(@NonNull NotificationEntry entry) { // this method is not responsible for intent sending. // will focus follow operation only after drag-and-drop that notification. final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true); @@ -534,10 +530,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } @Override - public void startNotificationGutsIntent(final Intent intent, final int appUid, - ExpandableNotificationRow row) { - expectNotNull(TAG, "intent", intent); - expectNotNull(TAG, "row", row); + public void startNotificationGutsIntent(@NonNull final Intent intent, final int appUid, + @NonNull ExpandableNotificationRow row) { boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index 09e191dd1911..92d0ebecf8d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -32,6 +32,7 @@ import com.android.systemui.statusbar.core.StatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarOrchestrator import com.android.systemui.statusbar.core.StatusBarSimpleFragment import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore +import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore @@ -45,7 +46,7 @@ import dagger.multibindings.IntoMap import kotlinx.coroutines.CoroutineScope /** Similar in purpose to [StatusBarModule], but scoped only to phones */ -@Module +@Module(includes = [PrivacyDotViewControllerModule::class]) interface StatusBarPhoneModule { @Binds diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java index cec77c12a40b..1bb4e8c66ef1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java @@ -16,16 +16,15 @@ package com.android.systemui.statusbar.policy; import android.content.res.Configuration; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; /** * Common listener for configuration or subsets of configuration changes (like density or * font scaling), providing easy static dependence on these events. */ -public interface ConfigurationController extends CallbackController<ConfigurationListener> { - - /** Alert controller of a change in the configuration. */ - void onConfigurationChanged(Configuration newConfiguration); +public interface ConfigurationController extends CallbackController<ConfigurationListener>, + ConfigurationForwarder { /** Alert controller of a change in between light and dark themes. */ void notifyThemeChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index c256e6430af9..00116aa71246 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -40,6 +40,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.res.R; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; @@ -71,6 +72,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController { new UpdateMonitorCallback(); private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy; private final KeyguardUpdateMonitorLogger mLogger; + private final Lazy<KeyguardInteractor> mKeyguardInteractorLazy; private boolean mCanDismissLockScreen; private boolean mShowing; @@ -123,6 +125,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController { Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController, KeyguardUpdateMonitorLogger logger, DumpManager dumpManager, + Lazy<KeyguardInteractor> keyguardInteractor, FeatureFlags featureFlags, SelectedUserInteractor userInteractor) { mContext = context; @@ -133,6 +136,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController { mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); mUnlockAnimationControllerLazy = keyguardUnlockAnimationController; mFeatureFlags = featureFlags; + mKeyguardInteractorLazy = keyguardInteractor; dumpManager.registerDumpable(getClass().getSimpleName(), this); @@ -354,6 +358,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController { Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardGoingAway", keyguardGoingAway ? 1 : 0); mKeyguardGoingAway = keyguardGoingAway; + mKeyguardInteractorLazy.get().setIsKeyguardGoingAway(keyguardGoingAway); invokeForEachCallback(Callback::onKeyguardGoingAwayChanged); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index b81af86b0779..c7bd5a1bb9a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -37,6 +37,7 @@ import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.NetworkControllerImpl; import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.statusbar.policy.BatteryControllerLogger; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.BluetoothControllerImpl; @@ -186,6 +187,13 @@ public interface StatusBarPolicyModule { DevicePostureControllerImpl devicePostureControllerImpl); /** */ + @Binds + @SysUISingleton + @GlobalConfig + ConfigurationForwarder provideGlobalConfigurationForwarder( + @GlobalConfig ConfigurationController configurationController); + + /** */ @Provides @SysUISingleton @GlobalConfig diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt index e89a31f8fad3..618722a79a6f 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt @@ -27,7 +27,10 @@ import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenCon import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureRecognizer +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @Composable fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -48,7 +51,17 @@ fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni ), ) val recognizer = rememberBackGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.map { + it.toGestureUiState( + progressStartMark = "", + progressEndMark = "", + successAnimation = R.raw.trackpad_back_success, + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable 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 7899f5b42a25..11e1ff49074a 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 @@ -17,6 +17,7 @@ package com.android.systemui.touchpad.tutorial.ui.composable import androidx.activity.compose.BackHandler +import androidx.annotation.RawRes import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Box @@ -31,23 +32,49 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInteropFilter +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.inputdevice.tutorial.ui.composable.ActionTutorialContent import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig +import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished +import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.EasterEggGestureMonitor import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer 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.InProgress -import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureHandler +import kotlinx.coroutines.flow.Flow -fun GestureState.toTutorialActionState(): TutorialActionState { +sealed interface GestureUiState { + data object NotStarted : GestureUiState + + data class Finished(@RawRes val successAnimation: Int) : GestureUiState + + data class InProgress( + val progress: Float = 0f, + val progressStartMark: String = "", + val progressEndMark: String = "", + ) : GestureUiState +} + +fun GestureState.toGestureUiState( + progressStartMark: String, + progressEndMark: String, + successAnimation: Int, +): GestureUiState { + return when (this) { + GestureState.NotStarted -> NotStarted + is GestureState.InProgress -> + GestureUiState.InProgress(this.progress, progressStartMark, progressEndMark) + is GestureState.Finished -> GestureUiState.Finished(successAnimation) + } +} + +fun GestureUiState.toTutorialActionState(): TutorialActionState { return when (this) { NotStarted -> TutorialActionState.NotStarted // progress is disabled for now as views are not ready to handle varying progress - is InProgress -> TutorialActionState.InProgress(0f) - Finished -> TutorialActionState.Finished + is GestureUiState.InProgress -> TutorialActionState.InProgress(progress = 0f) + is Finished -> TutorialActionState.Finished } } @@ -55,15 +82,13 @@ fun GestureState.toTutorialActionState(): TutorialActionState { fun GestureTutorialScreen( screenConfig: TutorialScreenConfig, gestureRecognizer: GestureRecognizer, + gestureUiStateFlow: Flow<GestureUiState>, onDoneButtonClicked: () -> Unit, onBack: () -> Unit, ) { BackHandler(onBack = onBack) - var gestureState: GestureState by remember { mutableStateOf(NotStarted) } var easterEggTriggered by remember { mutableStateOf(false) } - LaunchedEffect(gestureRecognizer) { - gestureRecognizer.addGestureStateCallback { gestureState = it } - } + val gestureState by gestureUiStateFlow.collectAsStateWithLifecycle(NotStarted) val easterEggMonitor = EasterEggGestureMonitor { easterEggTriggered = true } val gestureHandler = remember(gestureRecognizer) { TouchpadGestureHandler(gestureRecognizer, easterEggMonitor) } @@ -84,7 +109,7 @@ fun GestureTutorialScreen( @Composable private fun TouchpadGesturesHandlingBox( gestureHandler: TouchpadGestureHandler, - gestureState: GestureState, + gestureState: GestureUiState, easterEggTriggered: Boolean, resetEasterEggFlag: () -> Unit, modifier: Modifier = Modifier, @@ -110,7 +135,7 @@ private fun TouchpadGesturesHandlingBox( .pointerInteropFilter( onTouchEvent = { event -> // FINISHED is the final state so we don't need to process touches anymore - if (gestureState == Finished) { + if (gestureState is Finished) { false } else { gestureHandler.onMotionEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt index 3ddf760b9704..05871ce91e39 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt @@ -25,8 +25,11 @@ import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureRecognizer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @Composable fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -47,7 +50,17 @@ fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni ), ) val recognizer = rememberHomeGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.map { + it.toGestureUiState( + progressStartMark = "", + progressEndMark = "", + successAnimation = R.raw.trackpad_home_success, + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt index 30a21bf3b632..4fd16448e9f2 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt @@ -25,8 +25,11 @@ import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @Composable fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -47,7 +50,17 @@ fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () ), ) val recognizer = rememberRecentAppsGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.map { + it.toGestureUiState( + progressStartMark = "", + progressEndMark = "", + successAnimation = R.raw.trackpad_recent_apps_success, + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt index 80f800390852..024048cbbb24 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt @@ -33,6 +33,10 @@ class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt new file mode 100644 index 000000000000..23e31b0a9efd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt @@ -0,0 +1,30 @@ +/* + * 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.gesture + +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +class GestureFlowAdapter(gestureRecognizer: GestureRecognizer) { + + val gestureStateAsFlow: Flow<GestureState> = conflatedCallbackFlow { + val callback: (GestureState) -> Unit = { trySend(it) } + gestureRecognizer.addGestureStateCallback(callback) + awaitClose { gestureRecognizer.clearGestureStateCallback() } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt index d146268304a6..68a2ef9528eb 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt @@ -22,6 +22,8 @@ import java.util.function.Consumer /** Based on passed [MotionEvent]s recognizes different states of gesture and notifies callback. */ interface GestureRecognizer : Consumer<MotionEvent> { fun addGestureStateCallback(callback: (GestureState) -> Unit) + + fun clearGestureStateCallback() } fun isThreeFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 3) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt index 2b84a4c50613..b804b9a0d4c7 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt @@ -29,6 +29,10 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt index 69b7c5edd750..7d484ee85b7c 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt @@ -38,6 +38,10 @@ class RecentAppsGestureRecognizer( gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 7c5116dbf72c..07509e6368fb 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -64,6 +64,8 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.RotateDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; import android.media.AudioManager; import android.media.AudioSystem; import android.os.Debug; @@ -115,6 +117,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.view.RotationPolicy; import com.android.settingslib.Utils; import com.android.systemui.Dumpable; +import com.android.systemui.Flags; import com.android.systemui.Prefs; import com.android.systemui.dump.DumpManager; import com.android.systemui.haptics.slider.HapticSliderViewBinder; @@ -140,6 +143,7 @@ import com.android.systemui.volume.domain.interactor.VolumePanelNavigationIntera import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; import com.android.systemui.volume.ui.navigation.VolumeNavigator; +import com.google.android.msdl.domain.MSDLPlayer; import com.google.common.collect.ImmutableList; import dagger.Lazy; @@ -315,6 +319,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, private final Lazy<SecureSettings> mSecureSettings; private int mDialogTimeoutMillis; private final VibratorHelper mVibratorHelper; + private final MSDLPlayer mMSDLPlayer; private final com.android.systemui.util.time.SystemClock mSystemClock; private final VolumePanelFlag mVolumePanelFlag; private final VolumeDialogInteractor mInteractor; @@ -340,12 +345,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, DumpManager dumpManager, Lazy<SecureSettings> secureSettings, VibratorHelper vibratorHelper, + MSDLPlayer msdlPlayer, com.android.systemui.util.time.SystemClock systemClock, VolumeDialogInteractor interactor) { mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); mHandler = new H(looper); mVibratorHelper = vibratorHelper; + mMSDLPlayer = msdlPlayer; mSystemClock = systemClock; mShouldListenForJank = shouldListenForJank; mController = volumeDialogController; @@ -652,6 +659,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mRingerIcon = mRinger.findViewById(R.id.ringer_icon); } + if (Flags.hideRingerButtonInSingleVolumeMode() && AudioSystem.isSingleVolume(mContext)) { + mRingerAndDrawerContainer.setVisibility(INVISIBLE); + mRinger.setVisibility(INVISIBLE); + } + mSelectedRingerIcon = mDialog.findViewById(R.id.volume_new_ringer_active_icon); mSelectedRingerContainer = mDialog.findViewById( R.id.volume_new_ringer_active_icon_container); @@ -927,7 +939,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } private void addSliderHapticsToRow(VolumeRow row) { - row.createPlugin(mVibratorHelper, mSystemClock); + row.createPlugin(mVibratorHelper, mMSDLPlayer, mSystemClock); HapticSliderViewBinder.bind(row.slider, row.mHapticPlugin); } @@ -2337,10 +2349,31 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, return; } - final ColorDrawable solidDrawable = new ColorDrawable( + LayerDrawable background; + // mRingerAndDrawerContainer has rounded corner. + // But when it's not visible, mTopContainer needs to have rounded corner. + if (Flags.hideRingerButtonInSingleVolumeMode() + && mRingerAndDrawerContainer.getVisibility() != VISIBLE + ) { + float[] radius = new float[] { + mDialogCornerRadius, mDialogCornerRadius, // Top-left corner + mDialogCornerRadius, mDialogCornerRadius, // Top-right corner + 0, 0, // Bottom-right corner + 0, 0 // Bottom-left corner + }; + + ShapeDrawable roundedDrawable = new ShapeDrawable( + new RoundRectShape(radius, null, null)); + roundedDrawable.getPaint().setColor(Utils.getColorAttrDefaultColor( + mContext, com.android.internal.R.attr.colorSurface)); + + background = new LayerDrawable(new Drawable[] { roundedDrawable }); + } else { + final ColorDrawable solidDrawable = new ColorDrawable( Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorSurface)); - final LayerDrawable background = new LayerDrawable(new Drawable[] { solidDrawable }); + background = new LayerDrawable(new Drawable[] { solidDrawable }); + } // Size the solid color to match the primary volume row. In landscape, extend it upwards // slightly so that it fills in the bottom corners of the ringer icon, whose background is @@ -2707,11 +2740,13 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, void createPlugin( VibratorHelper vibratorHelper, + MSDLPlayer msdlPlayer, com.android.systemui.util.time.SystemClock systemClock) { if (mHapticPlugin != null) return; mHapticPlugin = new SeekbarHapticPlugin( vibratorHelper, + msdlPlayer, systemClock, sSliderHapticFeedbackConfig, sSliderTrackerConfig); diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index ed8de69ec482..2009143859d3 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -50,6 +50,8 @@ import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFact import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; import com.android.systemui.volume.ui.navigation.VolumeNavigator; +import com.google.android.msdl.domain.MSDLPlayer; + import dagger.Binds; import dagger.Lazy; import dagger.Module; @@ -121,6 +123,7 @@ public interface VolumeModule { DumpManager dumpManager, Lazy<SecureSettings> secureSettings, VibratorHelper vibratorHelper, + MSDLPlayer msdlPlayer, SystemClock systemClock, VolumeDialogInteractor interactor) { if (Flags.volumeRedesign()) { @@ -144,6 +147,7 @@ public interface VolumeModule { dumpManager, secureSettings, vibratorHelper, + msdlPlayer, systemClock, interactor); impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false); diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt new file mode 100644 index 000000000000..7265b82148ba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt @@ -0,0 +1,84 @@ +/* + * 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.volume.dialog.ringer.domain + +import android.media.AudioManager +import android.media.AudioManager.RINGER_MODE_NORMAL +import android.media.AudioManager.RINGER_MODE_SILENT +import android.media.AudioManager.RINGER_MODE_VIBRATE +import android.provider.Settings +import com.android.settingslib.volume.shared.model.RingerMode +import com.android.systemui.plugins.VolumeDialogController +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog +import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor +import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.stateIn + +/** Exposes [VolumeDialogRingerModel]. */ +@VolumeDialog +class VolumeDialogRingerInteractor +@Inject +constructor( + @VolumeDialog private val coroutineScope: CoroutineScope, + volumeDialogStateInteractor: VolumeDialogStateInteractor, + private val controller: VolumeDialogController, +) { + + val ringerModel: Flow<VolumeDialogRingerModel> = + volumeDialogStateInteractor.volumeDialogState + .mapNotNull { toRingerModel(it) } + .stateIn(coroutineScope, SharingStarted.Eagerly, null) + .filterNotNull() + + private fun toRingerModel(state: VolumeDialogStateModel): VolumeDialogRingerModel? { + return state.streamModels[AudioManager.STREAM_RING]?.let { + VolumeDialogRingerModel( + availableModes = + mutableListOf(RingerMode(RINGER_MODE_NORMAL), RingerMode(RINGER_MODE_SILENT)) + .also { list -> + if (controller.hasVibrator()) { + list.add(RingerMode(RINGER_MODE_VIBRATE)) + } + }, + currentRingerMode = RingerMode(state.ringerModeInternal), + isEnabled = + !(state.zenMode == Settings.Global.ZEN_MODE_ALARMS || + state.zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS || + (state.zenMode == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS && + state.disallowRinger)), + isMuted = it.level == 0 || it.muted, + level = it.level, + levelMax = it.levelMax, + ) + } + } + + fun setRingerMode(ringerMode: RingerMode) { + controller.setRingerMode(ringerMode.value, false) + } + + fun scheduleTouchFeedback() { + controller.scheduleTouchFeedback() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt new file mode 100644 index 000000000000..cf23f1a985f7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt @@ -0,0 +1,34 @@ +/* + * 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.volume.dialog.ringer.shared.model + +import com.android.settingslib.volume.shared.model.RingerMode + +/** Models the state of the volume dialog ringer. */ +data class VolumeDialogRingerModel( + val availableModes: List<RingerMode>, + /** Current ringer mode internal */ + val currentRingerMode: RingerMode, + /** whether the ringer is allowed given the current ZenMode */ + val isEnabled: Boolean, + /** Whether the current ring stream level is zero or the controller state is muted */ + val isMuted: Boolean, + /** Ring stream level */ + val level: Int, + /** Ring stream maximum level */ + val levelMax: Int, +) diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 02d0b577feb1..8039e00159f0 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -281,13 +281,13 @@ public final class WMShell implements void initSplitScreen(SplitScreen splitScreen) { mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() { @Override - public void onFinishedWakingUp() { - splitScreen.onFinishedWakingUp(); + public void onStartedGoingToSleep() { + splitScreen.onStartedGoingToSleep(); } @Override - public void onStartedGoingToSleep() { - splitScreen.onStartedGoingToSleep(); + public void onStartedWakingUp() { + splitScreen.onStartedWakingUp(); } }); mCommandQueue.addCallback(new CommandQueue.Callbacks() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 3d1a0d0cef3c..96f4a60271d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -481,6 +481,25 @@ public class DozeTriggersTest extends SysuiTestCase { verify(mAuthController).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); } + @Test + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void udfpsLongPress_triggeredWhenDoze() { + // GIVEN device is DOZE + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + // WHEN udfps long-press is triggered + mTriggers.onSensor(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, 100, 100, + new float[]{0, 1, 2, 3, 4}); + + // THEN the pulse is NOT dropped + verify(mDozeLog, never()).tracePulseDropped(anyString(), any()); + + // WHEN the screen state is OFF + mTriggers.onScreenState(Display.STATE_OFF); + + // THEN aod interrupt never be sent + verify(mAuthController, never()).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); + } @Test public void udfpsLongPress_dozeState_notRegistered() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt index 6b607400edfd..7383faf2fd78 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt @@ -21,6 +21,9 @@ import android.app.role.mockRoleManager import android.view.KeyEvent import android.view.KeyboardShortcutGroup import android.view.KeyboardShortcutInfo +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Tv +import androidx.compose.material.icons.filled.VerticalSplit import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -41,13 +44,17 @@ import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcut import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper import com.android.systemui.keyboard.shortcut.shortcutHelperViewModel +import com.android.systemui.keyboard.shortcut.ui.model.IconSource +import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.model.sysUiState +import com.android.systemui.settings.FakeUserTracker import com.android.systemui.settings.fakeUserTracker +import com.android.systemui.settings.userTracker import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -76,6 +83,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource() it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource() it.shortcutHelperCurrentAppShortcutsSource = fakeCurrentAppsSource + it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { context }) } private val testScope = kosmos.testScope @@ -253,7 +261,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { whenever( mockRoleManager.getRoleHoldersAsUser( RoleManager.ROLE_HOME, - fakeUserTracker.userHandle + fakeUserTracker.userHandle, ) ) .thenReturn(listOf(TestShortcuts.currentAppPackageName)) @@ -283,15 +291,25 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { val activeUiState = uiState as ShortcutsUiState.Active assertThat(activeUiState.shortcutCategories) .containsExactly( - ShortcutCategory( - System, - subCategoryWithShortcutLabels("first Foo shortcut1"), - subCategoryWithShortcutLabels("second foO shortcut2") + ShortcutCategoryUi( + label = "System", + iconSource = IconSource(imageVector = Icons.Default.Tv), + shortcutCategory = + ShortcutCategory( + System, + subCategoryWithShortcutLabels("first Foo shortcut1"), + subCategoryWithShortcutLabels("second foO shortcut2"), + ), + ), + ShortcutCategoryUi( + label = "Multitasking", + iconSource = IconSource(imageVector = Icons.Default.VerticalSplit), + shortcutCategory = + ShortcutCategory( + MultiTasking, + subCategoryWithShortcutLabels("third FoO shortcut1"), + ), ), - ShortcutCategory( - MultiTasking, - subCategoryWithShortcutLabels("third FoO shortcut1") - ) ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java index e981d627b582..cad22d360508 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java @@ -16,11 +16,11 @@ package com.android.systemui.keyguard; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.argThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt index dcf32a5f574d..51c852531697 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt @@ -38,11 +38,11 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.mock import org.mockito.Mockito.never @@ -61,7 +61,7 @@ private const val SESSION_TITLE = "SESSION_TITLE" private const val SMARTSPACE_KEY = "SMARTSPACE_KEY" private fun <T> anyObject(): T { - return Mockito.anyObject<T>() + return ArgumentMatchers.any<T>() } @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index bdd8dc8875e9..2aa300df4f7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java @@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java index 2f41ac17892d..338ed7596bd6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java @@ -19,9 +19,8 @@ import static android.provider.Settings.Global.SHOW_USB_TEMPERATURE_ALARM; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -194,7 +193,7 @@ public class PowerUITest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(1)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_SKIN)); } @Test @@ -207,7 +206,7 @@ public class PowerUITest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(1)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_USB_PORT)); } @Test @@ -220,7 +219,7 @@ public class PowerUITest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(0)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_SKIN)); } @Test @@ -233,7 +232,7 @@ public class PowerUITest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(0)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_USB_PORT)); } @Test @@ -243,14 +242,14 @@ public class PowerUITest extends SysuiTestCase { // success registering skin thermal event listener when(mThermalServiceMock.registerThermalEventListenerWithType( - anyObject(), eq(Temperature.TYPE_SKIN))).thenReturn(true); + any(), eq(Temperature.TYPE_SKIN))).thenReturn(true); mPowerUI.doSkinThermalEventListenerRegistration(); // verify registering skin thermal event listener, return true (success) TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(1)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_SKIN)); // Settings SHOW_TEMPERATURE_WARNING is set to 0 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 0); @@ -259,7 +258,7 @@ public class PowerUITest extends SysuiTestCase { // verify unregistering skin thermal event listener TestableLooper.get(this).processAllMessages(); - verify(mThermalServiceMock, times(1)).unregisterThermalEventListener(anyObject()); + verify(mThermalServiceMock, times(1)).unregisterThermalEventListener(any()); } @Test @@ -269,14 +268,14 @@ public class PowerUITest extends SysuiTestCase { // fail registering skin thermal event listener when(mThermalServiceMock.registerThermalEventListenerWithType( - anyObject(), eq(Temperature.TYPE_SKIN))).thenReturn(false); + any(), eq(Temperature.TYPE_SKIN))).thenReturn(false); mPowerUI.doSkinThermalEventListenerRegistration(); // verify registering skin thermal event listener, return false (fail) TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(1)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_SKIN)); // Settings SHOW_TEMPERATURE_WARNING is set to 0 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 0); @@ -285,7 +284,7 @@ public class PowerUITest extends SysuiTestCase { // verify that cannot unregister listener (current state is unregistered) TestableLooper.get(this).processAllMessages(); - verify(mThermalServiceMock, times(0)).unregisterThermalEventListener(anyObject()); + verify(mThermalServiceMock, times(0)).unregisterThermalEventListener(any()); // Settings SHOW_TEMPERATURE_WARNING is set to 1 Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); @@ -295,7 +294,7 @@ public class PowerUITest extends SysuiTestCase { // verify that can register listener (current state is unregistered) TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(2)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_SKIN)); } @Test @@ -305,14 +304,14 @@ public class PowerUITest extends SysuiTestCase { // success registering usb thermal event listener when(mThermalServiceMock.registerThermalEventListenerWithType( - anyObject(), eq(Temperature.TYPE_USB_PORT))).thenReturn(true); + any(), eq(Temperature.TYPE_USB_PORT))).thenReturn(true); mPowerUI.doUsbThermalEventListenerRegistration(); // verify registering usb thermal event listener, return true (success) TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(1)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_USB_PORT)); // Settings SHOW_USB_TEMPERATURE_ALARM is set to 0 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 0); @@ -320,7 +319,7 @@ public class PowerUITest extends SysuiTestCase { // verify unregistering usb thermal event listener mPowerUI.doUsbThermalEventListenerRegistration(); TestableLooper.get(this).processAllMessages(); - verify(mThermalServiceMock, times(1)).unregisterThermalEventListener(anyObject()); + verify(mThermalServiceMock, times(1)).unregisterThermalEventListener(any()); } @Test @@ -330,14 +329,14 @@ public class PowerUITest extends SysuiTestCase { // fail registering usb thermal event listener when(mThermalServiceMock.registerThermalEventListenerWithType( - anyObject(), eq(Temperature.TYPE_USB_PORT))).thenReturn(false); + any(), eq(Temperature.TYPE_USB_PORT))).thenReturn(false); mPowerUI.doUsbThermalEventListenerRegistration(); // verify registering usb thermal event listener, return false (fail) TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(1)) - .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); + .registerThermalEventListenerWithType(any(), eq(Temperature.TYPE_USB_PORT)); // Settings SHOW_USB_TEMPERATURE_ALARM is set to 0 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 0); @@ -346,7 +345,7 @@ public class PowerUITest extends SysuiTestCase { // verify that cannot unregister listener (current state is unregistered) TestableLooper.get(this).processAllMessages(); - verify(mThermalServiceMock, times(0)).unregisterThermalEventListener(anyObject()); + verify(mThermalServiceMock, times(0)).unregisterThermalEventListener(any()); // Settings SHOW_USB_TEMPERATURE_ALARM is set to 1 Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); @@ -356,7 +355,7 @@ public class PowerUITest extends SysuiTestCase { // verify that can register listener (current state is unregistered) TestableLooper.get(this).processAllMessages(); verify(mThermalServiceMock, times(2)).registerThermalEventListenerWithType( - anyObject(), eq(Temperature.TYPE_USB_PORT)); + any(), eq(Temperature.TYPE_USB_PORT)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index dad65f5add42..f6d57325bb1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -25,7 +25,7 @@ import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 748c7d9d939b..296478be77e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -36,7 +36,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Matchers.argThat; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt deleted file mode 100644 index 3756ec14503c..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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. - */ - -package com.android.systemui.screenshot - -import android.app.ActivityTaskManager.RootTaskInfo -import android.app.IActivityTaskManager -import android.content.ComponentName -import android.content.Context -import android.graphics.Rect -import android.os.UserHandle -import android.os.UserManager -import android.testing.AndroidTestingRunner -import com.android.systemui.SysuiTestCase -import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo -import com.android.systemui.screenshot.policy.ActivityType.Home -import com.android.systemui.screenshot.policy.ActivityType.Undefined -import com.android.systemui.screenshot.policy.WindowingMode.FullScreen -import com.android.systemui.screenshot.policy.WindowingMode.PictureInPicture -import com.android.systemui.screenshot.policy.newChildTask -import com.android.systemui.screenshot.policy.newRootTaskInfo -import com.android.systemui.settings.FakeDisplayTracker -import com.android.systemui.util.mockito.mock -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import org.junit.Test -import org.junit.runner.RunWith - -// The following values are chosen to be distinct from commonly seen real values -private const val DISPLAY_ID = 100 -private const val PRIMARY_USER = 2000 -private const val MANAGED_PROFILE_USER = 3000 - -@RunWith(AndroidTestingRunner::class) -class ScreenshotPolicyImplTest : SysuiTestCase() { - - @Test - fun testToDisplayContentInfo() { - assertThat(fullScreenWorkProfileTask.toDisplayContentInfo()) - .isEqualTo( - DisplayContentInfo( - ComponentName( - "com.google.android.apps.nbu.files", - "com.google.android.apps.nbu.files.home.HomeActivity" - ), - Rect(0, 0, 1080, 2400), - UserHandle.of(MANAGED_PROFILE_USER), - 65 - ) - ) - } - - @Test - fun findPrimaryContent_ignoresPipTask() = runBlocking { - val policy = - fakeTasksPolicyImpl( - mContext, - shadeExpanded = false, - tasks = listOf(pipTask, fullScreenWorkProfileTask, launcherTask, emptyTask) - ) - - val info = policy.findPrimaryContent(DISPLAY_ID) - assertThat(info).isEqualTo(fullScreenWorkProfileTask.toDisplayContentInfo()) - } - - @Test - fun findPrimaryContent_shadeExpanded_ignoresTopTask() = runBlocking { - val policy = - fakeTasksPolicyImpl( - mContext, - shadeExpanded = true, - tasks = listOf(fullScreenWorkProfileTask, launcherTask, emptyTask) - ) - - val info = policy.findPrimaryContent(DISPLAY_ID) - assertThat(info).isEqualTo(policy.systemUiContent) - } - - @Test - fun findPrimaryContent_emptyTaskList() = runBlocking { - val policy = fakeTasksPolicyImpl(mContext, shadeExpanded = false, tasks = listOf()) - - val info = policy.findPrimaryContent(DISPLAY_ID) - assertThat(info).isEqualTo(policy.systemUiContent) - } - - @Test - fun findPrimaryContent_workProfileNotOnTop() = runBlocking { - val policy = - fakeTasksPolicyImpl( - mContext, - shadeExpanded = false, - tasks = listOf(launcherTask, fullScreenWorkProfileTask, emptyTask) - ) - - val info = policy.findPrimaryContent(DISPLAY_ID) - assertThat(info).isEqualTo(launcherTask.toDisplayContentInfo()) - } - - private fun fakeTasksPolicyImpl( - context: Context, - shadeExpanded: Boolean, - tasks: List<RootTaskInfo> - ): ScreenshotPolicyImpl { - val userManager = mock<UserManager>() - val atmService = mock<IActivityTaskManager>() - val dispatcher = Dispatchers.Unconfined - val displayTracker = FakeDisplayTracker(context) - - return object : - ScreenshotPolicyImpl(context, userManager, atmService, dispatcher, displayTracker) { - override suspend fun isManagedProfile(userId: Int) = (userId == MANAGED_PROFILE_USER) - override suspend fun getAllRootTaskInfosOnDisplay(displayId: Int) = tasks - override suspend fun isNotificationShadeExpanded() = shadeExpanded - } - } - - private val pipTask = - newRootTaskInfo( - taskId = 66, - userId = PRIMARY_USER, - displayId = DISPLAY_ID, - bounds = Rect(628, 1885, 1038, 2295), - windowingMode = PictureInPicture, - topActivity = ComponentName.unflattenFromString(YOUTUBE_PIP_ACTIVITY), - ) { - listOf(newChildTask(taskId = 66, userId = 0, name = YOUTUBE_HOME_ACTIVITY)) - } - - private val fullScreenWorkProfileTask = - newRootTaskInfo( - taskId = 65, - userId = MANAGED_PROFILE_USER, - displayId = DISPLAY_ID, - bounds = Rect(0, 0, 1080, 2400), - windowingMode = FullScreen, - topActivity = ComponentName.unflattenFromString(FILES_HOME_ACTIVITY), - ) { - listOf( - newChildTask(taskId = 65, userId = MANAGED_PROFILE_USER, name = FILES_HOME_ACTIVITY) - ) - } - private val launcherTask = - newRootTaskInfo( - taskId = 1, - userId = PRIMARY_USER, - displayId = DISPLAY_ID, - activityType = Home, - windowingMode = FullScreen, - bounds = Rect(0, 0, 1080, 2400), - topActivity = ComponentName.unflattenFromString(LAUNCHER_ACTIVITY), - ) { - listOf(newChildTask(taskId = 1, userId = 0, name = LAUNCHER_ACTIVITY)) - } - - private val emptyTask = - newRootTaskInfo( - taskId = 2, - userId = PRIMARY_USER, - displayId = DISPLAY_ID, - visible = false, - running = false, - numActivities = 0, - activityType = Undefined, - bounds = Rect(0, 0, 1080, 2400), - ) { - listOf( - newChildTask(taskId = 3, name = ""), - newChildTask(taskId = 4, name = ""), - ) - } -} - -private const val YOUTUBE_HOME_ACTIVITY = - "com.google.android.youtube/" + "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity" - -private const val FILES_HOME_ACTIVITY = - "com.google.android.apps.nbu.files/" + "com.google.android.apps.nbu.files.home.HomeActivity" - -private const val YOUTUBE_PIP_ACTIVITY = - "com.google.android.youtube/" + - "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity" - -private const val LAUNCHER_ACTIVITY = - "com.google.android.apps.nexuslauncher/" + - "com.google.android.apps.nexuslauncher.NexusLauncherActivity" diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt index 040a9e959094..8bd8b72e5527 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package systemui.shared.clocks.view import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java index e9222c3ef6ad..3ad0605f2781 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java @@ -19,8 +19,8 @@ package com.android.systemui.shared.plugins; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 2b5e014b2048..b730b3703515 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -21,8 +21,8 @@ import static android.inputmethodservice.InputMethodService.IME_ACTIVE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index a75d7b23a92c..da0029ff6746 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -21,6 +21,8 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; @@ -777,6 +779,24 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll } @Test + public void indicationAreaHidden_untilBatteryInfoArrives() { + createController(); + // level of -1 indicates missing info + BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_UNKNOWN, + -1 /* level */, BatteryManager.BATTERY_PLUGGED_WIRELESS, 100 /* health */, + 0 /* maxChargingWattage */, true /* present */); + + mController.setVisible(true); + mStatusBarStateListener.onDozingChanged(true); + reset(mIndicationArea); + + mController.getKeyguardCallback().onRefreshBatteryInfo(status); + // VISIBLE is always called first + verify(mIndicationArea).setVisibility(VISIBLE); + verify(mIndicationArea).setVisibility(GONE); + } + + @Test public void onRefreshBatteryInfo_computesChargingTime() throws RemoteException { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt index b18b7f8f8129..72ffa0ee0855 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt @@ -16,41 +16,39 @@ package com.android.systemui.statusbar.commandline -import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import android.testing.TestableLooper - +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase - +import java.io.PrintWriter +import java.io.StringWriter +import java.util.concurrent.Executor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyList import org.mockito.ArgumentMatchers.eq -import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -import java.io.PrintWriter -import java.io.StringWriter -import java.util.concurrent.Executor private fun <T> anyObject(): T { - return Mockito.anyObject<T>() + return any<T>() } private fun <T : Any> safeEq(value: T): T = eq(value) ?: value -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @SmallTest class CommandRegistryTest : SysuiTestCase() { lateinit var registry: CommandRegistry - val inLineExecutor = object : Executor { - override fun execute(command: Runnable) { - command.run() + val inLineExecutor = + object : Executor { + override fun execute(command: Runnable) { + command.run() + } } - } val writer: PrintWriter = PrintWriter(StringWriter()) @@ -83,11 +81,9 @@ class CommandRegistryTest : SysuiTestCase() { } class FakeCommand() : Command { - override fun execute(pw: PrintWriter, args: List<String>) { - } + override fun execute(pw: PrintWriter, args: List<String>) {} - override fun help(pw: PrintWriter) { - } + override fun help(pw: PrintWriter) {} } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java index 7bd77a62ffcd..5fce08b08137 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.connectivity; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import android.os.HandlerThread; import android.telephony.SubscriptionInfo; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java index 83dbfa0682ad..b00f9e9f01d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java @@ -25,10 +25,10 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; @@ -297,7 +297,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase { assertNotNull(mDefaultCallbackInWifiTracker); assertNotNull(mDefaultCallbackInNetworkController); verify(mMockCm, atLeastOnce()).registerNetworkCallback( - isA(NetworkRequest.class), callbackArg.capture(), isA(Handler.class)); + isA(NetworkRequest.class), callbackArg.capture(), + isA(Handler.class)); mNetworkCallback = callbackArg.getValue(); assertNotNull(mNetworkCallback); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index 0b5f8d5e948c..723c0d701305 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -74,22 +74,31 @@ import com.android.systemui.testKosmos import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.wmshell.BubblesManager import java.util.Optional -import junit.framework.Assert import kotlin.test.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runCurrent +import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.invocation.InvocationOnMock +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever /** Tests for [NotificationGutsManager] with the scene container enabled. */ +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @RunWithLooper @@ -99,7 +108,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { NotificationChannel( TEST_CHANNEL_ID, TEST_CHANNEL_ID, - NotificationManager.IMPORTANCE_DEFAULT + NotificationManager.IMPORTANCE_DEFAULT, ) private val kosmos = testKosmos() @@ -146,7 +155,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) allowTestableLooperAsMainThread() helper = NotificationTestHelper(mContext, mDependency) - Mockito.`when`(accessibilityManager.isTouchExplorationEnabled).thenReturn(false) + whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(false) windowRootViewVisibilityInteractor = WindowRootViewVisibilityInteractor( testScope.backgroundScope, @@ -185,12 +194,12 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { deviceProvisionedController, metricsLogger, headsUpManager, - activityStarter + activityStarter, ) gutsManager.setUpWithPresenter( presenter, notificationListContainer, - onSettingsClickListener + onSettingsClickListener, ) gutsManager.setNotificationActivityStarter(notificationActivityStarter) gutsManager.start() @@ -198,49 +207,31 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { @Test fun testOpenAndCloseGuts() { - val guts = Mockito.spy(NotificationGuts(mContext)) - Mockito.`when`(guts.post(ArgumentMatchers.any())).thenAnswer { invocation: InvocationOnMock - -> + val guts = spy(NotificationGuts(mContext)) + whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock -> handler.post((invocation.arguments[0] as Runnable)) null } // Test doesn't support animation since the guts view is not attached. - Mockito.doNothing() - .`when`(guts) - .openControls( - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.any(Runnable::class.java) - ) + doNothing() + .whenever(guts) + .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) val realRow = createTestNotificationRow() val menuItem = createTestMenuItem(realRow) - val row = Mockito.spy(realRow) - Mockito.`when`(row!!.windowToken).thenReturn(Binder()) - Mockito.`when`(row.guts).thenReturn(guts) + val row = spy(realRow) + whenever(row!!.windowToken).thenReturn(Binder()) + whenever(row.guts).thenReturn(guts) Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem)) assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong()) executor.runAllReady() - verify(guts) - .openControls( - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.any(Runnable::class.java) - ) + verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) verify(headsUpManager).setGutsShown(realRow!!.entry, true) assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong()) gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false) verify(guts) - .closeControls( - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean() - ) - verify(row, Mockito.times(1)).setGutsView(ArgumentMatchers.any()) + .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>()) + verify(row, times(1)).setGutsView(any()) executor.runAllReady() verify(headsUpManager).setGutsShown(realRow.entry, false) } @@ -250,7 +241,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { // First, start out lockscreen or shade as not visible setIsLockscreenOrShadeVisible(false) testScope.testScheduler.runCurrent() - val guts = Mockito.mock(NotificationGuts::class.java) + val guts = mock<NotificationGuts>() gutsManager.exposedGuts = guts // WHEN the lockscreen or shade becomes visible @@ -258,15 +249,9 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { testScope.testScheduler.runCurrent() // THEN the guts are not closed - verify(guts, Mockito.never()).removeCallbacks(ArgumentMatchers.any()) - verify(guts, Mockito.never()) - .closeControls( - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean() - ) + verify(guts, never()).removeCallbacks(any()) + verify(guts, never()) + .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>()) } @Test @@ -274,7 +259,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { // First, start out lockscreen or shade as visible setIsLockscreenOrShadeVisible(true) testScope.testScheduler.runCurrent() - val guts = Mockito.mock(NotificationGuts::class.java) + val guts = mock<NotificationGuts>() gutsManager.exposedGuts = guts // WHEN the lockscreen or shade is no longer visible @@ -282,14 +267,14 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { testScope.testScheduler.runCurrent() // THEN the guts are closed - verify(guts).removeCallbacks(ArgumentMatchers.any()) + verify(guts).removeCallbacks(anyOrNull()) verify(guts) .closeControls( - /* leavebehinds= */ ArgumentMatchers.eq(true), - /* controls= */ ArgumentMatchers.eq(true), - /* x= */ ArgumentMatchers.anyInt(), - /* y= */ ArgumentMatchers.anyInt(), - /* force= */ ArgumentMatchers.eq(true) + /* leavebehinds= */ eq(true), + /* controls= */ eq(true), + /* x= */ any<Int>(), + /* y= */ any<Int>(), + /* force= */ eq(true), ) } @@ -304,95 +289,68 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { testScope.testScheduler.runCurrent() // THEN the list container is reset - verify(notificationListContainer) - .resetExposedMenuView(ArgumentMatchers.anyBoolean(), ArgumentMatchers.anyBoolean()) + verify(notificationListContainer).resetExposedMenuView(any<Boolean>(), any<Boolean>()) } @Test fun testChangeDensityOrFontScale() { - val guts = Mockito.spy(NotificationGuts(mContext)) - Mockito.`when`(guts.post(ArgumentMatchers.any())).thenAnswer { invocation: InvocationOnMock - -> + val guts = spy(NotificationGuts(mContext)) + whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock -> handler.post((invocation.arguments[0] as Runnable)) null } // Test doesn't support animation since the guts view is not attached. - Mockito.doNothing() - .`when`(guts) - .openControls( - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.any(Runnable::class.java) - ) + doNothing() + .whenever(guts) + .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) val realRow = createTestNotificationRow() val menuItem = createTestMenuItem(realRow) - val row = Mockito.spy(realRow) - Mockito.`when`(row!!.windowToken).thenReturn(Binder()) - Mockito.`when`(row.guts).thenReturn(guts) - Mockito.doNothing().`when`(row).ensureGutsInflated() + val row = spy(realRow) + whenever(row!!.windowToken).thenReturn(Binder()) + whenever(row.guts).thenReturn(guts) + doNothing().whenever(row).ensureGutsInflated() val realEntry = realRow!!.entry - val entry = Mockito.spy(realEntry) - Mockito.`when`(entry.row).thenReturn(row) - Mockito.`when`(entry.getGuts()).thenReturn(guts) + val entry = spy(realEntry) + whenever(entry.row).thenReturn(row) + whenever(entry.getGuts()).thenReturn(guts) Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem)) executor.runAllReady() - verify(guts) - .openControls( - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.any(Runnable::class.java) - ) + verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) // called once by mGutsManager.bindGuts() in mGutsManager.openGuts() - verify(row).setGutsView(ArgumentMatchers.any()) + verify(row).setGutsView(any()) row.onDensityOrFontScaleChanged() gutsManager.onDensityOrFontScaleChanged(entry) executor.runAllReady() gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false) verify(guts) - .closeControls( - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean() - ) + .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>()) // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged() - verify(row, Mockito.times(2)).setGutsView(ArgumentMatchers.any()) + verify(row, times(2)).setGutsView(any()) } @Test fun testAppOpsSettingsIntent_camera() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_CAMERA) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action) } @Test fun testAppOpsSettingsIntent_mic() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_RECORD_AUDIO) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action) } @Test @@ -400,30 +358,22 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_CAMERA) ops.add(AppOpsManager.OP_RECORD_AUDIO) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action) } @Test fun testAppOpsSettingsIntent_overlay() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.lastValue.action) } @Test @@ -432,15 +382,11 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { ops.add(AppOpsManager.OP_CAMERA) ops.add(AppOpsManager.OP_RECORD_AUDIO) ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action) } @Test @@ -448,15 +394,11 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_CAMERA) ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action) } @Test @@ -464,112 +406,108 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_RECORD_AUDIO) ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action) } @Test @Throws(Exception::class) fun testInitializeNotificationInfoView_highPriority() { - val notificationInfoView = Mockito.mock(NotificationInfo::class.java) - val row = Mockito.spy(helper.createRow()) + val notificationInfoView = mock<NotificationInfo>() + val row = spy(helper.createRow()) val entry = row.entry NotificationEntryHelper.modifyRanking(entry) .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) .setImportance(NotificationManager.IMPORTANCE_HIGH) .build() - Mockito.`when`(row.getIsNonblockable()).thenReturn(false) - Mockito.`when`(highPriorityProvider.isHighPriority(entry)).thenReturn(true) + whenever(row.getIsNonblockable()).thenReturn(false) + whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true) val statusBarNotification = entry.sbn gutsManager.initializeNotificationInfo(row, notificationInfoView) verify(notificationInfoView) .bindNotification( - ArgumentMatchers.any(PackageManager::class.java), - ArgumentMatchers.any(INotificationManager::class.java), - ArgumentMatchers.eq(onUserInteractionCallback), - ArgumentMatchers.eq(channelEditorDialogController), - ArgumentMatchers.eq(statusBarNotification.packageName), - ArgumentMatchers.any(NotificationChannel::class.java), - ArgumentMatchers.eq(entry), - ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java), - ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java), - ArgumentMatchers.any(UiEventLogger::class.java), - ArgumentMatchers.eq(true), - ArgumentMatchers.eq(false), - ArgumentMatchers.eq(true), /* wasShownHighPriority */ - ArgumentMatchers.eq(assistantFeedbackController), - ArgumentMatchers.any(MetricsLogger::class.java) + any<PackageManager>(), + any<INotificationManager>(), + eq(onUserInteractionCallback), + eq(channelEditorDialogController), + eq(statusBarNotification.packageName), + any<NotificationChannel>(), + eq(entry), + any<NotificationInfo.OnSettingsClickListener>(), + any<NotificationInfo.OnAppSettingsClickListener>(), + any<UiEventLogger>(), + eq(true), + eq(false), + eq(true), /* wasShownHighPriority */ + eq(assistantFeedbackController), + any<MetricsLogger>(), ) } @Test @Throws(Exception::class) fun testInitializeNotificationInfoView_PassesAlongProvisionedState() { - val notificationInfoView = Mockito.mock(NotificationInfo::class.java) - val row = Mockito.spy(helper.createRow()) + val notificationInfoView = mock<NotificationInfo>() + val row = spy(helper.createRow()) NotificationEntryHelper.modifyRanking(row.entry) .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) .build() - Mockito.`when`(row.getIsNonblockable()).thenReturn(false) + whenever(row.getIsNonblockable()).thenReturn(false) val statusBarNotification = row.entry.sbn val entry = row.entry gutsManager.initializeNotificationInfo(row, notificationInfoView) verify(notificationInfoView) .bindNotification( - ArgumentMatchers.any(PackageManager::class.java), - ArgumentMatchers.any(INotificationManager::class.java), - ArgumentMatchers.eq(onUserInteractionCallback), - ArgumentMatchers.eq(channelEditorDialogController), - ArgumentMatchers.eq(statusBarNotification.packageName), - ArgumentMatchers.any(NotificationChannel::class.java), - ArgumentMatchers.eq(entry), - ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java), - ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java), - ArgumentMatchers.any(UiEventLogger::class.java), - ArgumentMatchers.eq(true), - ArgumentMatchers.eq(false), - ArgumentMatchers.eq(false), /* wasShownHighPriority */ - ArgumentMatchers.eq(assistantFeedbackController), - ArgumentMatchers.any(MetricsLogger::class.java) + any<PackageManager>(), + any<INotificationManager>(), + eq(onUserInteractionCallback), + eq(channelEditorDialogController), + eq(statusBarNotification.packageName), + any<NotificationChannel>(), + eq(entry), + any<NotificationInfo.OnSettingsClickListener>(), + any<NotificationInfo.OnAppSettingsClickListener>(), + any<UiEventLogger>(), + eq(true), + eq(false), + eq(false), /* wasShownHighPriority */ + eq(assistantFeedbackController), + any<MetricsLogger>(), ) } @Test @Throws(Exception::class) fun testInitializeNotificationInfoView_withInitialAction() { - val notificationInfoView = Mockito.mock(NotificationInfo::class.java) - val row = Mockito.spy(helper.createRow()) + val notificationInfoView = mock<NotificationInfo>() + val row = spy(helper.createRow()) NotificationEntryHelper.modifyRanking(row.entry) .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) .build() - Mockito.`when`(row.getIsNonblockable()).thenReturn(false) + whenever(row.getIsNonblockable()).thenReturn(false) val statusBarNotification = row.entry.sbn val entry = row.entry gutsManager.initializeNotificationInfo(row, notificationInfoView) verify(notificationInfoView) .bindNotification( - ArgumentMatchers.any(PackageManager::class.java), - ArgumentMatchers.any(INotificationManager::class.java), - ArgumentMatchers.eq(onUserInteractionCallback), - ArgumentMatchers.eq(channelEditorDialogController), - ArgumentMatchers.eq(statusBarNotification.packageName), - ArgumentMatchers.any(NotificationChannel::class.java), - ArgumentMatchers.eq(entry), - ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java), - ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java), - ArgumentMatchers.any(UiEventLogger::class.java), - ArgumentMatchers.eq(true), - ArgumentMatchers.eq(false), - ArgumentMatchers.eq(false), /* wasShownHighPriority */ - ArgumentMatchers.eq(assistantFeedbackController), - ArgumentMatchers.any(MetricsLogger::class.java) + any<PackageManager>(), + any<INotificationManager>(), + eq(onUserInteractionCallback), + eq(channelEditorDialogController), + eq(statusBarNotification.packageName), + any<NotificationChannel>(), + eq(entry), + any<NotificationInfo.OnSettingsClickListener>(), + any<NotificationInfo.OnAppSettingsClickListener>(), + any<UiEventLogger>(), + eq(true), + eq(false), + eq(false), /* wasShownHighPriority */ + eq(assistantFeedbackController), + any<MetricsLogger>(), ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt index dcd57f137d71..c2460f93b862 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt @@ -17,30 +17,27 @@ package com.android.systemui.statusbar.policy import android.app.NotificationManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper - +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest - import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock - import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock -import org.mockito.Mockito import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations private fun <T> anyObject(): T { - return Mockito.anyObject<T>() + return any<T>() } -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper() @SmallTest class BatteryStateNotifierTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 9bb760760cd7..f91f373dfc2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -22,10 +22,9 @@ import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.argThat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -120,9 +119,9 @@ public class SecurityControllerTest extends SysuiTestCase { verify(mBroadcastDispatcher).registerReceiverWithHandler( brCaptor.capture(), - anyObject(), - anyObject(), - anyObject()); + any(), + any(), + any()); mBroadcastReceiver = brCaptor.getValue(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index ecc7909a857a..3007eabba0b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -92,6 +92,7 @@ import com.android.systemui.volume.domain.interactor.VolumePanelNavigationIntera import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag; import com.android.systemui.volume.ui.navigation.VolumeNavigator; +import com.google.android.msdl.domain.MSDLPlayer; import com.google.common.collect.ImmutableList; import dagger.Lazy; @@ -169,6 +170,9 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Mock private VibratorHelper mVibratorHelper; + @Mock + private MSDLPlayer mMSDLPlayer; + private int mLongestHideShowAnimationDuration = 250; private FakeSettings mSecureSettings; @@ -222,6 +226,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mDumpManager, mLazySecureSettings, mVibratorHelper, + mMSDLPlayer, new FakeSystemClock(), mVolumeDialogInteractor); mDialog.init(0, null); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt index 1b1d8c5d0f63..c77d0aab653d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt @@ -28,6 +28,7 @@ import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer import com.android.systemui.haptics.msdl.bouncerHapticPlayer import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardMediaKeyInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.user.domain.interactor.selectedUserInteractor @@ -60,6 +61,7 @@ val Kosmos.bouncerSceneContentViewModel by Fixture { patternViewModelFactory = patternBouncerViewModelFactory, passwordViewModelFactory = passwordBouncerViewModelFactory, bouncerHapticPlayer = bouncerHapticPlayer, + keyguardMediaKeyInteractor = keyguardMediaKeyInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/FakeMSDLPlayer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/FakeMSDLPlayer.kt index 2b81da33b9bc..fe82ab9ff7ed 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/FakeMSDLPlayer.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/FakeMSDLPlayer.kt @@ -23,19 +23,24 @@ import com.google.android.msdl.domain.MSDLPlayer import com.google.android.msdl.logging.MSDLEvent class FakeMSDLPlayer : MSDLPlayer { + val tokensPlayed = mutableListOf<MSDLToken>() + val propertiesPlayed = mutableListOf<InteractionProperties?>() private val history = arrayListOf<MSDLEvent>() + var currentFeedbackLevel = FeedbackLevel.DEFAULT var latestTokenPlayed: MSDLToken? = null + get() = tokensPlayed.lastOrNull() private set var latestPropertiesPlayed: InteractionProperties? = null + get() = propertiesPlayed.lastOrNull() private set override fun getSystemFeedbackLevel(): FeedbackLevel = currentFeedbackLevel override fun playToken(token: MSDLToken, properties: InteractionProperties?) { - latestTokenPlayed = token - latestPropertiesPlayed = properties + tokensPlayed.add(token) + propertiesPlayed.add(properties) history.add(MSDLEvent(token, properties)) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt index 257d758fe6e6..3fbcf7773eb7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.haptics.slider import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.interaction.InteractionSource +import com.android.systemui.haptics.msdl.msdlPlayer import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.haptics.vibratorHelper import com.android.systemui.kosmos.Kosmos @@ -40,6 +41,7 @@ val Kosmos.sliderHapticsViewModelFactory by sliderHapticFeedbackConfig, sliderTrackerConfig, vibratorHelper, + msdlPlayer, fakeSystemClock, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt index fbfaba60ebf0..c41493eaa9c7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt @@ -41,7 +41,7 @@ import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.model.sysUiState import com.android.systemui.settings.displayTracker -import com.android.systemui.settings.fakeUserTracker +import com.android.systemui.settings.userTracker var Kosmos.shortcutHelperAppCategoriesShortcutsSource: KeyboardShortcutGroupsSource by Kosmos.Fixture { AppCategoriesShortcutsSource(windowManager, testDispatcher) } @@ -114,7 +114,7 @@ val Kosmos.shortcutHelperViewModel by Kosmos.Fixture { ShortcutHelperViewModel( mockRoleManager, - fakeUserTracker, + userTracker, applicationCoroutineScope, testDispatcher, shortcutHelperStateInteractor, 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 e513e8d2a350..08786495eca4 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 @@ -88,9 +88,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { private val _isDreamingWithOverlay = MutableStateFlow(false) override val isDreamingWithOverlay: Flow<Boolean> = _isDreamingWithOverlay - private val _isActiveDreamLockscreenHosted = MutableStateFlow(false) - override val isActiveDreamLockscreenHosted: StateFlow<Boolean> = _isActiveDreamLockscreenHosted - private val _dozeAmount = MutableStateFlow(0f) override val linearDozeAmount: Flow<Float> = _dozeAmount @@ -102,8 +99,7 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { private val _isUdfpsSupported = MutableStateFlow(false) - private val _isKeyguardGoingAway = MutableStateFlow(false) - override val isKeyguardGoingAway: Flow<Boolean> = _isKeyguardGoingAway + override val isKeyguardGoingAway = MutableStateFlow(false) private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel(BiometricUnlockMode.NONE, null)) @@ -169,7 +165,7 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { } fun setKeyguardGoingAway(isGoingAway: Boolean) { - _isKeyguardGoingAway.value = isGoingAway + isKeyguardGoingAway.value = isGoingAway } fun setKeyguardOccluded(isOccluded: Boolean) { @@ -235,10 +231,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { _isDreamingWithOverlay.value = isDreaming } - override fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean) { - _isActiveDreamLockscreenHosted.value = isLockscreenHosted - } - fun setDozeAmount(dozeAmount: Float) { _dozeAmount.value = dozeAmount } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractorKosmos.kt new file mode 100644 index 000000000000..6f4787b0290b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardMediaKeyInteractorKosmos.kt @@ -0,0 +1,29 @@ +/* + * 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.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.telephony.domain.interactor.telephonyInteractor +import com.android.systemui.volume.data.repository.audioRepository + +val Kosmos.keyguardMediaKeyInteractor: KeyguardMediaKeyInteractor by + Kosmos.Fixture { + KeyguardMediaKeyInteractor( + telephonyInteractor = telephonyInteractor, + audioRepository = audioRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt index 8b7e5d8f54c5..88063c9b5db9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/BrightnessSliderControllerKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.settings import com.android.internal.logging.uiEventLogger import com.android.systemui.classifier.falsingManager +import com.android.systemui.haptics.msdl.msdlPlayer import com.android.systemui.haptics.vibratorHelper import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.activityStarter @@ -31,6 +32,7 @@ var Kosmos.brightnessSliderControllerFactory by falsingManager, uiEventLogger, vibratorHelper, + msdlPlayer, systemClock, activityStarter, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index 7f4c670b05aa..c3996e400726 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -85,7 +85,6 @@ import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent import com.android.systemui.util.Assert.runWithCurrentThreadAsMainThread import com.android.systemui.util.DeviceConfigProxyFake import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.wmshell.BubblesManager @@ -126,6 +125,7 @@ class ExpandableNotificationRowBuilder( private val mMainCoroutineContext = mTestScope.coroutineContext private val mFakeSystemClock = FakeSystemClock() private val mMainExecutor = FakeExecutor(mFakeSystemClock) + private val mDumpManager = DumpManager() init { featureFlags.setDefault(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE) @@ -142,8 +142,7 @@ class ExpandableNotificationRowBuilder( mGroupMembershipManager = GroupMembershipManagerImpl() mSmartReplyController = Mockito.mock(SmartReplyController::class.java, STUB_ONLY) - val dumpManager = DumpManager() - mGroupExpansionManager = GroupExpansionManagerImpl(dumpManager, mGroupMembershipManager) + mGroupExpansionManager = GroupExpansionManagerImpl(mDumpManager, mGroupMembershipManager) mHeadsUpManager = Mockito.mock(HeadsUpManager::class.java, STUB_ONLY) mIconManager = IconManager( @@ -289,8 +288,8 @@ class ExpandableNotificationRowBuilder( NotificationOptimizedLinearLayoutFactory(), { Mockito.mock(NotificationViewFlipperFactory::class.java) }, NotificationRowIconViewInflaterFactory( - AppIconProviderImpl(context), - NotificationIconStyleProviderImpl(), + AppIconProviderImpl(context, mDumpManager), + NotificationIconStyleProviderImpl(mDumpManager), ), ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt index 08c6bbab6dd6..0fd0f1469818 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.notification.row.icon import android.content.applicationContext +import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos -val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext) } +val Kosmos.appIconProvider by + Kosmos.Fixture { AppIconProviderImpl(applicationContext, dumpManager) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt index 611c90a6f4e8..0fe84fb135ab 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.row.icon +import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos -val Kosmos.notificationIconStyleProvider by Kosmos.Fixture { NotificationIconStyleProviderImpl() } +val Kosmos.notificationIconStyleProvider by + Kosmos.Fixture { NotificationIconStyleProviderImpl(dumpManager) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt index 6be13be407d8..32191277c94a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt @@ -23,7 +23,7 @@ class FakeConfigurationController @Inject constructor() : listeners -= listener } - override fun onConfigurationChanged(newConfiguration: Configuration?) { + override fun onConfigurationChanged(newConfiguration: Configuration) { listeners.forEach { it.onConfigChanged(newConfiguration) } } @@ -36,7 +36,7 @@ class FakeConfigurationController @Inject constructor() : } fun notifyConfigurationChanged() { - onConfigurationChanged(newConfiguration = null) + onConfigurationChanged(newConfiguration = Configuration()) } fun notifyLayoutDirectionChanged(isRtl: Boolean) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt index bca13c6f502d..65247a55348d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt @@ -24,6 +24,6 @@ class FakeStatusBarWindowControllerFactory : StatusBarWindowController.Factory { override fun create( context: Context, viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, - statusBarConfigurationController: StatusBarConfigurationController + statusBarConfigurationController: StatusBarConfigurationController, ) = FakeStatusBarWindowController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioRepositoryKosmos.kt index 5cf214a4e04a..712ec41bbf2d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioRepositoryKosmos.kt @@ -18,4 +18,5 @@ package com.android.systemui.volume.data.repository import com.android.systemui.kosmos.Kosmos -val Kosmos.audioRepository by Kosmos.Fixture { FakeAudioRepository() } +val Kosmos.fakeAudioRepository by Kosmos.Fixture { FakeAudioRepository() } +val Kosmos.audioRepository by Kosmos.Fixture { fakeAudioRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt index ba6ffd742611..16d2a18cd7b2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.volume.data.repository import android.media.AudioDeviceInfo import android.media.AudioManager +import android.view.KeyEvent import com.android.settingslib.volume.data.model.VolumeControllerEvent import com.android.settingslib.volume.data.repository.AudioRepository import com.android.settingslib.volume.shared.model.AudioStream @@ -61,6 +62,15 @@ class FakeAudioRepository : AudioRepository { val isInitialized: Boolean get() = mutableIsInitialized + private val _dispatchedKeyEvents = mutableListOf<KeyEvent>() + + val dispatchedKeyEvents: List<KeyEvent> + get() { + val currentValue = _dispatchedKeyEvents.toList() + _dispatchedKeyEvents.clear() + return currentValue + } + override fun init() { mutableIsInitialized = true } @@ -145,4 +155,8 @@ class FakeAudioRepository : AudioRepository { mutableIsVolumeControllerVisible.value = isVisible } } + + override fun dispatchMediaKeyEvent(event: KeyEvent) { + _dispatchedKeyEvents.add(event) + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt new file mode 100644 index 000000000000..c2a1544820c5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * 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.volume.dialog.ringer.domain + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.plugins.volumeDialogController +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor + +val Kosmos.volumeDialogRingerInteractor by + Kosmos.Fixture { + VolumeDialogRingerInteractor( + coroutineScope = applicationCoroutineScope, + volumeDialogStateInteractor = volumeDialogStateInteractor, + controller = volumeDialogController, + ) + } diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index d9182010c1cb..ff2abd275f83 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -100,6 +100,9 @@ java_library_host { srcs: [ "runtime-helper-src/libcore-fake/**/*.java", ], + libs: [ + "app-compat-annotations", + ], static_libs: [ "ravenwood-runtime-common", ], @@ -121,6 +124,7 @@ java_library { ], static_libs: [ "ravenwood-runtime-common", + "androidx.annotation_annotation", ], libs: [ "framework-minus-apex.ravenwood", diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java index 3535cb2b1b79..870a10a1f57e 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java @@ -42,6 +42,10 @@ public class RavenwoodConfigState { private final RavenwoodConfig mConfig; + // TODO: Move the other contexts from RavenwoodConfig to here too? They're used by + // RavenwoodRule too, but RavenwoodRule can probably use InstrumentationRegistry? + RavenwoodContext mSystemServerContext; + public RavenwoodConfigState(RavenwoodConfig config) { mConfig = config; } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 9a145cb93811..c2806daf99a1 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -16,6 +16,8 @@ package android.platform.test.ravenwood; +import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACKAGE_NAME; + import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; @@ -267,6 +269,13 @@ public class RavenwoodRuntimeEnvironmentController { config.mInstContext = instContext; config.mTargetContext = targetContext; + final Supplier<Resources> systemResourcesLoader = () -> { + return config.mState.loadResources(null); + }; + + config.mState.mSystemServerContext = + new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader); + // Prepare other fields. config.mInstrumentation = new Instrumentation(); config.mInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation()); @@ -314,6 +323,9 @@ public class RavenwoodRuntimeEnvironmentController { ((RavenwoodContext) config.mTargetContext).cleanUp(); config.mTargetContext = null; } + if (config.mState.mSystemServerContext != null) { + config.mState.mSystemServerContext.cleanUp(); + } Looper.getMainLooper().quit(); Looper.clearMainLooperForTest(); diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java index 3946dd8471b0..f198a08a50e3 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java @@ -33,6 +33,9 @@ import java.util.List; import java.util.Set; public class RavenwoodSystemServer { + + static final String ANDROID_PACKAGE_NAME = "android"; + /** * Set of services that we know how to provide under Ravenwood. We keep this set distinct * from {@code com.android.server.SystemServer} to give us the ability to choose either @@ -67,7 +70,7 @@ public class RavenwoodSystemServer { sStartedServices = new ArraySet<>(); sTimings = new TimingsTraceAndSlog(); - sServiceManager = new SystemServiceManager(config.mInstContext); + sServiceManager = new SystemServiceManager(config.mState.mSystemServerContext); sServiceManager.setStartInfo(false, SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java index 1f6e11dd5cf2..37b0abcceede 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java @@ -67,6 +67,7 @@ public final class RavenwoodConfig { String mTargetPackageName; int mMinSdkLevel; + int mTargetSdkLevel; boolean mProvideMainThread = false; @@ -150,6 +151,14 @@ public final class RavenwoodConfig { } /** + * Configure the target SDK level of the test. + */ + public Builder setTargetSdkLevel(int sdkLevel) { + mConfig.mTargetSdkLevel = sdkLevel; + return this; + } + + /** * Configure a "main" thread to be available for the duration of the test, as defined * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments. * diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java index ced151927fdc..9bc45bee1775 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java @@ -146,6 +146,9 @@ public class RavenwoodSystemProperties { if (root.startsWith("soc.")) return true; if (root.startsWith("system.")) return true; + // For PropertyInvalidatedCache + if (root.startsWith("cache_key.")) return true; + switch (key) { case "gsm.version.baseband": case "no.such.thing": @@ -170,6 +173,9 @@ public class RavenwoodSystemProperties { if (root.startsWith("debug.")) return true; + // For PropertyInvalidatedCache + if (root.startsWith("cache_key.")) return true; + return mKeyWritable.contains(key); } diff --git a/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java b/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java new file mode 100644 index 000000000000..1e3b3fcd733f --- /dev/null +++ b/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java @@ -0,0 +1,1035 @@ +/* + * Copyright (C) 2019 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 android.util; + +// [ravenwood] This is an exact copy from StatsD, until we make StatsD available on Ravenwood. + +import static java.nio.charset.StandardCharsets.UTF_8; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Build; +import android.os.SystemClock; + +import androidx.annotation.RequiresApi; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +/** + * StatsEvent builds and stores the buffer sent over the statsd socket. + * This class defines and encapsulates the socket protocol. + * + * <p>Usage:</p> + * <pre> + * // Pushed event + * StatsEvent statsEvent = StatsEvent.newBuilder() + * .setAtomId(atomId) + * .writeBoolean(false) + * .writeString("annotated String field") + * .addBooleanAnnotation(annotationId, true) + * .usePooledBuffer() + * .build(); + * StatsLog.write(statsEvent); + * + * // Pulled event + * StatsEvent statsEvent = StatsEvent.newBuilder() + * .setAtomId(atomId) + * .writeBoolean(false) + * .writeString("annotated String field") + * .addBooleanAnnotation(annotationId, true) + * .build(); + * </pre> + * @hide + **/ +@SystemApi +public final class StatsEvent { + // Type Ids. + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_INT = 0x00; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_LONG = 0x01; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_STRING = 0x02; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_LIST = 0x03; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_FLOAT = 0x04; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_BOOLEAN = 0x05; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_BYTE_ARRAY = 0x06; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_OBJECT = 0x07; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_KEY_VALUE_PAIRS = 0x08; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_ATTRIBUTION_CHAIN = 0x09; + + /** + * @hide + **/ + @VisibleForTesting + public static final byte TYPE_ERRORS = 0x0F; + + // Error flags. + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_NO_TIMESTAMP = 0x1; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_NO_ATOM_ID = 0x2; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_OVERFLOW = 0x4; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_ATTRIBUTION_CHAIN_TOO_LONG = 0x8; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_TOO_MANY_KEY_VALUE_PAIRS = 0x10; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD = 0x20; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_INVALID_ANNOTATION_ID = 0x40; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_ANNOTATION_ID_TOO_LARGE = 0x80; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_TOO_MANY_ANNOTATIONS = 0x100; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_TOO_MANY_FIELDS = 0x200; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000; + + /** + * @hide + **/ + @VisibleForTesting + public static final int ERROR_ATOM_ID_INVALID_POSITION = 0x2000; + + /** + * @hide + **/ + @VisibleForTesting public static final int ERROR_LIST_TOO_LONG = 0x4000; + + // Size limits. + + /** + * @hide + **/ + @VisibleForTesting + public static final int MAX_ANNOTATION_COUNT = 15; + + /** + * @hide + **/ + @VisibleForTesting + public static final int MAX_ATTRIBUTION_NODES = 127; + + /** + * @hide + **/ + @VisibleForTesting + public static final int MAX_NUM_ELEMENTS = 127; + + /** + * @hide + **/ + @VisibleForTesting + public static final int MAX_KEY_VALUE_PAIRS = 127; + + private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068; + + // Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag. + // See android_util_StatsLog.cpp. + private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4; + + private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB + + private final int mAtomId; + private final byte[] mPayload; + private Buffer mBuffer; + private final int mNumBytes; + + private StatsEvent(final int atomId, @Nullable final Buffer buffer, + @NonNull final byte[] payload, final int numBytes) { + mAtomId = atomId; + mBuffer = buffer; + mPayload = payload; + mNumBytes = numBytes; + } + + /** + * Returns a new StatsEvent.Builder for building StatsEvent object. + **/ + @NonNull + public static Builder newBuilder() { + return new Builder(Buffer.obtain()); + } + + /** + * Get the atom Id of the atom encoded in this StatsEvent object. + * + * @hide + **/ + public int getAtomId() { + return mAtomId; + } + + /** + * Get the byte array that contains the encoded payload that can be sent to statsd. + * + * @hide + **/ + @NonNull + public byte[] getBytes() { + return mPayload; + } + + /** + * Get the number of bytes used to encode the StatsEvent payload. + * + * @hide + **/ + public int getNumBytes() { + return mNumBytes; + } + + /** + * Recycle resources used by this StatsEvent object. + * No actions should be taken on this StatsEvent after release() is called. + * + * @hide + **/ + public void release() { + if (mBuffer != null) { + mBuffer.release(); + mBuffer = null; + } + } + + /** + * Builder for constructing a StatsEvent object. + * + * <p>This class defines and encapsulates the socket encoding for the + *buffer. The write methods must be called in the same order as the order of + *fields in the atom definition.</p> + * + * <p>setAtomId() must be called immediately after + *StatsEvent.newBuilder().</p> + * + * <p>Example:</p> + * <pre> + * // Atom definition. + * message MyAtom { + * optional int32 field1 = 1; + * optional int64 field2 = 2; + * optional string field3 = 3 [(annotation1) = true]; + * optional repeated int32 field4 = 4; + * } + * + * // StatsEvent construction for pushed event. + * StatsEvent.newBuilder() + * StatsEvent statsEvent = StatsEvent.newBuilder() + * .setAtomId(atomId) + * .writeInt(3) // field1 + * .writeLong(8L) // field2 + * .writeString("foo") // field 3 + * .addBooleanAnnotation(annotation1Id, true) + * .writeIntArray({ 1, 2, 3 }); + * .usePooledBuffer() + * .build(); + * + * // StatsEvent construction for pulled event. + * StatsEvent.newBuilder() + * StatsEvent statsEvent = StatsEvent.newBuilder() + * .setAtomId(atomId) + * .writeInt(3) // field1 + * .writeLong(8L) // field2 + * .writeString("foo") // field 3 + * .addBooleanAnnotation(annotation1Id, true) + * .writeIntArray({ 1, 2, 3 }); + * .build(); + * </pre> + **/ + public static final class Builder { + // Fixed positions. + private static final int POS_NUM_ELEMENTS = 1; + private static final int POS_TIMESTAMP_NS = POS_NUM_ELEMENTS + Byte.BYTES; + private static final int POS_ATOM_ID = POS_TIMESTAMP_NS + Byte.BYTES + Long.BYTES; + + private final Buffer mBuffer; + private long mTimestampNs; + private int mAtomId; + private byte mCurrentAnnotationCount; + private int mPos; + private int mPosLastField; + private byte mLastType; + private int mNumElements; + private int mErrorMask; + private boolean mUsePooledBuffer = false; + + private Builder(final Buffer buffer) { + mBuffer = buffer; + mCurrentAnnotationCount = 0; + mAtomId = 0; + mTimestampNs = SystemClock.elapsedRealtimeNanos(); + mNumElements = 0; + + // Set mPos to 0 for writing TYPE_OBJECT at 0th position. + mPos = 0; + writeTypeId(TYPE_OBJECT); + + // Write timestamp. + mPos = POS_TIMESTAMP_NS; + writeLong(mTimestampNs); + } + + /** + * Sets the atom id for this StatsEvent. + * + * This should be called immediately after StatsEvent.newBuilder() + * and should only be called once. + * Not calling setAtomId will result in ERROR_NO_ATOM_ID. + * Calling setAtomId out of order will result in ERROR_ATOM_ID_INVALID_POSITION. + **/ + @NonNull + public Builder setAtomId(final int atomId) { + if (0 == mAtomId) { + mAtomId = atomId; + + if (1 == mNumElements) { // Only timestamp is written so far. + writeInt(atomId); + } else { + // setAtomId called out of order. + mErrorMask |= ERROR_ATOM_ID_INVALID_POSITION; + } + } + + return this; + } + + /** + * Write a boolean field to this StatsEvent. + **/ + @NonNull + public Builder writeBoolean(final boolean value) { + // Write boolean typeId byte followed by boolean byte representation. + writeTypeId(TYPE_BOOLEAN); + mPos += mBuffer.putBoolean(mPos, value); + mNumElements++; + return this; + } + + /** + * Write an integer field to this StatsEvent. + **/ + @NonNull + public Builder writeInt(final int value) { + // Write integer typeId byte followed by 4-byte representation of value. + writeTypeId(TYPE_INT); + mPos += mBuffer.putInt(mPos, value); + mNumElements++; + return this; + } + + /** + * Write a long field to this StatsEvent. + **/ + @NonNull + public Builder writeLong(final long value) { + // Write long typeId byte followed by 8-byte representation of value. + writeTypeId(TYPE_LONG); + mPos += mBuffer.putLong(mPos, value); + mNumElements++; + return this; + } + + /** + * Write a float field to this StatsEvent. + **/ + @NonNull + public Builder writeFloat(final float value) { + // Write float typeId byte followed by 4-byte representation of value. + writeTypeId(TYPE_FLOAT); + mPos += mBuffer.putFloat(mPos, value); + mNumElements++; + return this; + } + + /** + * Write a String field to this StatsEvent. + **/ + @NonNull + public Builder writeString(@NonNull final String value) { + // Write String typeId byte, followed by 4-byte representation of number of bytes + // in the UTF-8 encoding, followed by the actual UTF-8 byte encoding of value. + final byte[] valueBytes = stringToBytes(value); + writeByteArray(valueBytes, TYPE_STRING); + return this; + } + + /** + * Write a byte array field to this StatsEvent. + **/ + @NonNull + public Builder writeByteArray(@NonNull final byte[] value) { + // Write byte array typeId byte, followed by 4-byte representation of number of bytes + // in value, followed by the actual byte array. + writeByteArray(value, TYPE_BYTE_ARRAY); + return this; + } + + private void writeByteArray(@NonNull final byte[] value, final byte typeId) { + writeTypeId(typeId); + final int numBytes = value.length; + mPos += mBuffer.putInt(mPos, numBytes); + mPos += mBuffer.putByteArray(mPos, value); + mNumElements++; + } + + /** + * Write an attribution chain field to this StatsEvent. + * + * The sizes of uids and tags must be equal. The AttributionNode at position i is + * made up of uids[i] and tags[i]. + * + * @param uids array of uids in the attribution nodes. + * @param tags array of tags in the attribution nodes. + **/ + @NonNull + public Builder writeAttributionChain( + @NonNull final int[] uids, @NonNull final String[] tags) { + final byte numUids = (byte) uids.length; + final byte numTags = (byte) tags.length; + + if (numUids != numTags) { + mErrorMask |= ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL; + } else if (numUids > MAX_ATTRIBUTION_NODES) { + mErrorMask |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG; + } else { + // Write attribution chain typeId byte, followed by 1-byte representation of + // number of attribution nodes, followed by encoding of each attribution node. + writeTypeId(TYPE_ATTRIBUTION_CHAIN); + mPos += mBuffer.putByte(mPos, numUids); + for (int i = 0; i < numUids; i++) { + // Each uid is encoded as 4-byte representation of its int value. + mPos += mBuffer.putInt(mPos, uids[i]); + + // Each tag is encoded as 4-byte representation of number of bytes in its + // UTF-8 encoding, followed by the actual UTF-8 bytes. + final byte[] tagBytes = stringToBytes(tags[i]); + mPos += mBuffer.putInt(mPos, tagBytes.length); + mPos += mBuffer.putByteArray(mPos, tagBytes); + } + mNumElements++; + } + return this; + } + + /** + * Write KeyValuePairsAtom entries to this StatsEvent. + * + * @param intMap Integer key-value pairs. + * @param longMap Long key-value pairs. + * @param stringMap String key-value pairs. + * @param floatMap Float key-value pairs. + **/ + @NonNull + public Builder writeKeyValuePairs( + @Nullable final SparseIntArray intMap, + @Nullable final SparseLongArray longMap, + @Nullable final SparseArray<String> stringMap, + @Nullable final SparseArray<Float> floatMap) { + final int intMapSize = null == intMap ? 0 : intMap.size(); + final int longMapSize = null == longMap ? 0 : longMap.size(); + final int stringMapSize = null == stringMap ? 0 : stringMap.size(); + final int floatMapSize = null == floatMap ? 0 : floatMap.size(); + final int totalCount = intMapSize + longMapSize + stringMapSize + floatMapSize; + + if (totalCount > MAX_KEY_VALUE_PAIRS) { + mErrorMask |= ERROR_TOO_MANY_KEY_VALUE_PAIRS; + } else { + writeTypeId(TYPE_KEY_VALUE_PAIRS); + mPos += mBuffer.putByte(mPos, (byte) totalCount); + + for (int i = 0; i < intMapSize; i++) { + final int key = intMap.keyAt(i); + final int value = intMap.valueAt(i); + mPos += mBuffer.putInt(mPos, key); + writeTypeId(TYPE_INT); + mPos += mBuffer.putInt(mPos, value); + } + + for (int i = 0; i < longMapSize; i++) { + final int key = longMap.keyAt(i); + final long value = longMap.valueAt(i); + mPos += mBuffer.putInt(mPos, key); + writeTypeId(TYPE_LONG); + mPos += mBuffer.putLong(mPos, value); + } + + for (int i = 0; i < stringMapSize; i++) { + final int key = stringMap.keyAt(i); + final String value = stringMap.valueAt(i); + mPos += mBuffer.putInt(mPos, key); + writeTypeId(TYPE_STRING); + final byte[] valueBytes = stringToBytes(value); + mPos += mBuffer.putInt(mPos, valueBytes.length); + mPos += mBuffer.putByteArray(mPos, valueBytes); + } + + for (int i = 0; i < floatMapSize; i++) { + final int key = floatMap.keyAt(i); + final float value = floatMap.valueAt(i); + mPos += mBuffer.putInt(mPos, key); + writeTypeId(TYPE_FLOAT); + mPos += mBuffer.putFloat(mPos, value); + } + + mNumElements++; + } + + return this; + } + + /** + * Write a repeated boolean field to this StatsEvent. + * + * The list size must not exceed 127. Otherwise, the array isn't written + * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the + * StatsEvent errors field. + * + * @param elements array of booleans. + **/ + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @NonNull + public Builder writeBooleanArray(@NonNull final boolean[] elements) { + final byte numElements = (byte)elements.length; + + if (writeArrayInfo(numElements, TYPE_BOOLEAN)) { + // Write encoding of each element. + for (int i = 0; i < numElements; i++) { + mPos += mBuffer.putBoolean(mPos, elements[i]); + } + mNumElements++; + } + return this; + } + + /** + * Write a repeated int field to this StatsEvent. + * + * The list size must not exceed 127. Otherwise, the array isn't written + * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the + * StatsEvent errors field. + * + * @param elements array of ints. + **/ + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @NonNull + public Builder writeIntArray(@NonNull final int[] elements) { + final byte numElements = (byte)elements.length; + + if (writeArrayInfo(numElements, TYPE_INT)) { + // Write encoding of each element. + for (int i = 0; i < numElements; i++) { + mPos += mBuffer.putInt(mPos, elements[i]); + } + mNumElements++; + } + return this; + } + + /** + * Write a repeated long field to this StatsEvent. + * + * The list size must not exceed 127. Otherwise, the array isn't written + * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the + * StatsEvent errors field. + * + * @param elements array of longs. + **/ + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @NonNull + public Builder writeLongArray(@NonNull final long[] elements) { + final byte numElements = (byte)elements.length; + + if (writeArrayInfo(numElements, TYPE_LONG)) { + // Write encoding of each element. + for (int i = 0; i < numElements; i++) { + mPos += mBuffer.putLong(mPos, elements[i]); + } + mNumElements++; + } + return this; + } + + /** + * Write a repeated float field to this StatsEvent. + * + * The list size must not exceed 127. Otherwise, the array isn't written + * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the + * StatsEvent errors field. + * + * @param elements array of floats. + **/ + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @NonNull + public Builder writeFloatArray(@NonNull final float[] elements) { + final byte numElements = (byte)elements.length; + + if (writeArrayInfo(numElements, TYPE_FLOAT)) { + // Write encoding of each element. + for (int i = 0; i < numElements; i++) { + mPos += mBuffer.putFloat(mPos, elements[i]); + } + mNumElements++; + } + return this; + } + + /** + * Write a repeated string field to this StatsEvent. + * + * The list size must not exceed 127. Otherwise, the array isn't written + * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the + * StatsEvent errors field. + * + * @param elements array of strings. + **/ + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @NonNull + public Builder writeStringArray(@NonNull final String[] elements) { + final byte numElements = (byte)elements.length; + + if (writeArrayInfo(numElements, TYPE_STRING)) { + // Write encoding of each element. + for (int i = 0; i < numElements; i++) { + final byte[] elementBytes = stringToBytes(elements[i]); + mPos += mBuffer.putInt(mPos, elementBytes.length); + mPos += mBuffer.putByteArray(mPos, elementBytes); + } + mNumElements++; + } + return this; + } + + /** + * Write a boolean annotation for the last field written. + **/ + @NonNull + public Builder addBooleanAnnotation( + final byte annotationId, final boolean value) { + // Ensure there's a field written to annotate. + if (mNumElements < 2) { + mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD; + } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) { + mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS; + } else { + mPos += mBuffer.putByte(mPos, annotationId); + mPos += mBuffer.putByte(mPos, TYPE_BOOLEAN); + mPos += mBuffer.putBoolean(mPos, value); + mCurrentAnnotationCount++; + writeAnnotationCount(); + } + + return this; + } + + /** + * Write an integer annotation for the last field written. + **/ + @NonNull + public Builder addIntAnnotation(final byte annotationId, final int value) { + if (mNumElements < 2) { + mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD; + } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) { + mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS; + } else { + mPos += mBuffer.putByte(mPos, annotationId); + mPos += mBuffer.putByte(mPos, TYPE_INT); + mPos += mBuffer.putInt(mPos, value); + mCurrentAnnotationCount++; + writeAnnotationCount(); + } + + return this; + } + + /** + * Indicates to reuse Buffer's byte array as the underlying payload in StatsEvent. + * This should be called for pushed events to reduce memory allocations and garbage + * collections. + **/ + @NonNull + public Builder usePooledBuffer() { + mUsePooledBuffer = true; + mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos); + return this; + } + + /** + * Builds a StatsEvent object with values entered in this Builder. + **/ + @NonNull + public StatsEvent build() { + if (0L == mTimestampNs) { + mErrorMask |= ERROR_NO_TIMESTAMP; + } + if (0 == mAtomId) { + mErrorMask |= ERROR_NO_ATOM_ID; + } + if (mBuffer.hasOverflowed()) { + mErrorMask |= ERROR_OVERFLOW; + } + if (mNumElements > MAX_NUM_ELEMENTS) { + mErrorMask |= ERROR_TOO_MANY_FIELDS; + } + + if (0 == mErrorMask) { + mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements); + } else { + // Write atom id and error mask. Overwrite any annotations for atom Id. + mPos = POS_ATOM_ID; + mPos += mBuffer.putByte(mPos, TYPE_INT); + mPos += mBuffer.putInt(mPos, mAtomId); + mPos += mBuffer.putByte(mPos, TYPE_ERRORS); + mPos += mBuffer.putInt(mPos, mErrorMask); + mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3); + } + + final int size = mPos; + + if (mUsePooledBuffer) { + return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size); + } else { + // Create a copy of the buffer with the required number of bytes. + final byte[] payload = new byte[size]; + System.arraycopy(mBuffer.getBytes(), 0, payload, 0, size); + + // Return Buffer instance to the pool. + mBuffer.release(); + + return new StatsEvent(mAtomId, null, payload, size); + } + } + + private void writeTypeId(final byte typeId) { + mPosLastField = mPos; + mLastType = typeId; + mCurrentAnnotationCount = 0; + final byte encodedId = (byte) (typeId & 0x0F); + mPos += mBuffer.putByte(mPos, encodedId); + } + + private void writeAnnotationCount() { + // Use first 4 bits for annotation count and last 4 bits for typeId. + final byte encodedId = (byte) ((mCurrentAnnotationCount << 4) | (mLastType & 0x0F)); + mBuffer.putByte(mPosLastField, encodedId); + } + + @NonNull + private static byte[] stringToBytes(@Nullable final String value) { + return (null == value ? "" : value).getBytes(UTF_8); + } + + private boolean writeArrayInfo(final byte numElements, + final byte elementTypeId) { + if (numElements > MAX_NUM_ELEMENTS) { + mErrorMask |= ERROR_LIST_TOO_LONG; + return false; + } + // Write list typeId byte, 1-byte representation of number of + // elements, and element typeId byte. + writeTypeId(TYPE_LIST); + mPos += mBuffer.putByte(mPos, numElements); + // Write element typeId byte without setting mPosLastField and mLastType (i.e. don't use + // #writeTypeId) + final byte encodedId = (byte) (elementTypeId & 0x0F); + mPos += mBuffer.putByte(mPos, encodedId); + return true; + } + } + + private static final class Buffer { + private static Object sLock = new Object(); + + @GuardedBy("sLock") + private static Buffer sPool; + + private byte[] mBytes; + private boolean mOverflow = false; + private int mMaxSize = MAX_PULL_PAYLOAD_SIZE; + + @NonNull + private static Buffer obtain() { + final Buffer buffer; + synchronized (sLock) { + buffer = null == sPool ? new Buffer() : sPool; + sPool = null; + } + buffer.reset(); + return buffer; + } + + private Buffer() { + final ByteBuffer tempBuffer = ByteBuffer.allocateDirect(MAX_PUSH_PAYLOAD_SIZE); + mBytes = tempBuffer.hasArray() ? tempBuffer.array() : new byte [MAX_PUSH_PAYLOAD_SIZE]; + } + + @NonNull + private byte[] getBytes() { + return mBytes; + } + + private void release() { + // Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under. + if (mMaxSize <= MAX_PUSH_PAYLOAD_SIZE) { + synchronized (sLock) { + if (null == sPool) { + sPool = this; + } + } + } + } + + private void reset() { + mOverflow = false; + mMaxSize = MAX_PULL_PAYLOAD_SIZE; + } + + private void setMaxSize(final int maxSize, final int numBytesWritten) { + mMaxSize = maxSize; + if (numBytesWritten > maxSize) { + mOverflow = true; + } + } + + private boolean hasOverflowed() { + return mOverflow; + } + + /** + * Checks for available space in the byte array. + * + * @param index starting position in the buffer to start the check. + * @param numBytes number of bytes to check from index. + * @return true if space is available, false otherwise. + **/ + private boolean hasEnoughSpace(final int index, final int numBytes) { + final int totalBytesNeeded = index + numBytes; + + if (totalBytesNeeded > mMaxSize) { + mOverflow = true; + return false; + } + + // Expand buffer if needed. + if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) { + int newSize = mBytes.length; + do { + newSize *= 2; + } while (newSize <= totalBytesNeeded); + + if (newSize > mMaxSize) { + newSize = mMaxSize; + } + + mBytes = Arrays.copyOf(mBytes, newSize); + } + + return true; + } + + /** + * Writes a byte into the buffer. + * + * @param index position in the buffer where the byte is written. + * @param value the byte to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putByte(final int index, final byte value) { + if (hasEnoughSpace(index, Byte.BYTES)) { + mBytes[index] = (byte) (value); + return Byte.BYTES; + } + return 0; + } + + /** + * Writes a boolean into the buffer. + * + * @param index position in the buffer where the boolean is written. + * @param value the boolean to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putBoolean(final int index, final boolean value) { + return putByte(index, (byte) (value ? 1 : 0)); + } + + /** + * Writes an integer into the buffer. + * + * @param index position in the buffer where the integer is written. + * @param value the integer to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putInt(final int index, final int value) { + if (hasEnoughSpace(index, Integer.BYTES)) { + // Use little endian byte order. + mBytes[index] = (byte) (value); + mBytes[index + 1] = (byte) (value >> 8); + mBytes[index + 2] = (byte) (value >> 16); + mBytes[index + 3] = (byte) (value >> 24); + return Integer.BYTES; + } + return 0; + } + + /** + * Writes a long into the buffer. + * + * @param index position in the buffer where the long is written. + * @param value the long to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putLong(final int index, final long value) { + if (hasEnoughSpace(index, Long.BYTES)) { + // Use little endian byte order. + mBytes[index] = (byte) (value); + mBytes[index + 1] = (byte) (value >> 8); + mBytes[index + 2] = (byte) (value >> 16); + mBytes[index + 3] = (byte) (value >> 24); + mBytes[index + 4] = (byte) (value >> 32); + mBytes[index + 5] = (byte) (value >> 40); + mBytes[index + 6] = (byte) (value >> 48); + mBytes[index + 7] = (byte) (value >> 56); + return Long.BYTES; + } + return 0; + } + + /** + * Writes a float into the buffer. + * + * @param index position in the buffer where the float is written. + * @param value the float to write. + * @return number of bytes written to buffer from this write operation. + **/ + private int putFloat(final int index, final float value) { + return putInt(index, Float.floatToIntBits(value)); + } + + /** + * Copies a byte array into the buffer. + * + * @param index position in the buffer where the byte array is copied. + * @param value the byte array to copy. + * @return number of bytes written to buffer from this write operation. + **/ + private int putByteArray(final int index, @NonNull final byte[] value) { + final int numBytes = value.length; + if (hasEnoughSpace(index, numBytes)) { + System.arraycopy(value, 0, mBytes, index, numBytes); + return numBytes; + } + return 0; + } + } +} diff --git a/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java b/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java new file mode 100644 index 000000000000..c1c20cfac9dd --- /dev/null +++ b/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java @@ -0,0 +1,478 @@ +/* + * 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. + */ + +package android.util; + +/* + * [Ravenwood] This is copied from StatsD, with the following changes: + * - The static {} is commented out. + * - All references to IStatsD and StatsdStatsLog are commented out. + * - The native method is no-oped. + */ + +import static android.Manifest.permission.DUMP; +import static android.Manifest.permission.PACKAGE_USAGE_STATS; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Build; +//import android.os.IStatsd; +import android.os.Process; +import android.util.proto.ProtoOutputStream; + +import androidx.annotation.RequiresApi; + +//import com.android.internal.statsd.StatsdStatsLog; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * StatsLog provides an API for developers to send events to statsd. The events can be used to + * define custom metrics in side statsd. + */ +public final class StatsLog { + +// // Load JNI library +// static { +// System.loadLibrary("stats_jni"); +// } + private static final String TAG = "StatsLog"; + private static final boolean DEBUG = false; + private static final int EXPERIMENT_IDS_FIELD_ID = 1; + + /** + * Annotation ID constant for logging UID field. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + public static final byte ANNOTATION_ID_IS_UID = 1; + + /** + * Annotation ID constant to indicate logged atom event's timestamp should be truncated. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2; + + /** + * Annotation ID constant for a state atom's primary field. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + public static final byte ANNOTATION_ID_PRIMARY_FIELD = 3; + + /** + * Annotation ID constant for state atom's state field. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + public static final byte ANNOTATION_ID_EXCLUSIVE_STATE = 4; + + /** + * Annotation ID constant to indicate the first UID in the attribution chain + * is a primary field. + * Should only be used for attribution chain fields. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + public static final byte ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5; + + /** + * Annotation ID constant to indicate which state is default for the state atom. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + public static final byte ANNOTATION_ID_DEFAULT_STATE = 6; + + /** + * Annotation ID constant to signal all states should be reset to the default state. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + public static final byte ANNOTATION_ID_TRIGGER_STATE_RESET = 7; + + /** + * Annotation ID constant to indicate state changes need to account for nesting. + * This should only be used with binary state atoms. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + public static final byte ANNOTATION_ID_STATE_NESTED = 8; + + /** + * Annotation ID constant to indicate the restriction category of an atom. + * This annotation must only be attached to the atom id. This is an int annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_RESTRICTION_CATEGORY = 9; + + /** + * Annotation ID to indicate that a field of an atom contains peripheral device info. + * This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO = 10; + + /** + * Annotation ID to indicate that a field of an atom contains app usage information. + * This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE = 11; + + /** + * Annotation ID to indicate that a field of an atom contains app activity information. + * This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY = 12; + + /** + * Annotation ID to indicate that a field of an atom contains health connect information. + * This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT = 13; + + /** + * Annotation ID to indicate that a field of an atom contains accessibility information. + * This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY = 14; + + /** + * Annotation ID to indicate that a field of an atom contains system search information. + * This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH = 15; + + /** + * Annotation ID to indicate that a field of an atom contains user engagement information. + * This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT = 16; + + /** + * Annotation ID to indicate that a field of an atom contains ambient sensing information. + * This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING = 17; + + /** + * Annotation ID to indicate that a field of an atom contains demographic classification + * information. This is a bool annotation. + * + * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() + * accept byte as the type for annotation ids to save space. + * + * @hide + */ + @SuppressLint("NoByteOrShort") + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final byte ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION = 18; + + + /** @hide */ + @IntDef(prefix = { "RESTRICTION_CATEGORY_" }, value = { + RESTRICTION_CATEGORY_DIAGNOSTIC, + RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE, + RESTRICTION_CATEGORY_AUTHENTICATION, + RESTRICTION_CATEGORY_FRAUD_AND_ABUSE}) + @Retention(RetentionPolicy.SOURCE) + public @interface RestrictionCategory {} + + /** + * Restriction category for atoms about diagnostics. + * + * @hide + */ + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final int RESTRICTION_CATEGORY_DIAGNOSTIC = 1; + + /** + * Restriction category for atoms about system intelligence. + * + * @hide + */ + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final int RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE = 2; + + /** + * Restriction category for atoms about authentication. + * + * @hide + */ + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final int RESTRICTION_CATEGORY_AUTHENTICATION = 3; + + /** + * Restriction category for atoms about fraud and abuse. + * + * @hide + */ + @SystemApi + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final int RESTRICTION_CATEGORY_FRAUD_AND_ABUSE = 4; + + private StatsLog() { + } + + /** + * Logs a start event. + * + * @param label developer-chosen label. + * @return True if the log request was sent to statsd. + */ + public static boolean logStart(int label) { + int callingUid = Process.myUid(); +// StatsdStatsLog.write( +// StatsdStatsLog.APP_BREADCRUMB_REPORTED, +// callingUid, +// label, +// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START); + return true; + } + + /** + * Logs a stop event. + * + * @param label developer-chosen label. + * @return True if the log request was sent to statsd. + */ + public static boolean logStop(int label) { + int callingUid = Process.myUid(); +// StatsdStatsLog.write( +// StatsdStatsLog.APP_BREADCRUMB_REPORTED, +// callingUid, +// label, +// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); + return true; + } + + /** + * Logs an event that does not represent a start or stop boundary. + * + * @param label developer-chosen label. + * @return True if the log request was sent to statsd. + */ + public static boolean logEvent(int label) { + int callingUid = Process.myUid(); +// StatsdStatsLog.write( +// StatsdStatsLog.APP_BREADCRUMB_REPORTED, +// callingUid, +// label, +// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); + return true; + } + + /** + * Logs an event for binary push for module updates. + * + * @param trainName name of install train. + * @param trainVersionCode version code of the train. + * @param options optional flags about this install. + * The last 3 bits indicate options: + * 0x01: FLAG_REQUIRE_STAGING + * 0x02: FLAG_ROLLBACK_ENABLED + * 0x04: FLAG_REQUIRE_LOW_LATENCY_MONITOR + * @param state current install state. Defined as State enums in + * BinaryPushStateChanged atom in + * frameworks/proto_logging/stats/atoms.proto + * @param experimentIds experiment ids. + * @return True if the log request was sent to statsd. + */ + @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) + public static boolean logBinaryPushStateChanged(@NonNull String trainName, + long trainVersionCode, int options, int state, + @NonNull long[] experimentIds) { + ProtoOutputStream proto = new ProtoOutputStream(); + for (long id : experimentIds) { + proto.write( + ProtoOutputStream.FIELD_TYPE_INT64 + | ProtoOutputStream.FIELD_COUNT_REPEATED + | EXPERIMENT_IDS_FIELD_ID, + id); + } +// StatsdStatsLog.write(StatsdStatsLog.BINARY_PUSH_STATE_CHANGED, +// trainName, +// trainVersionCode, +// (options & IStatsd.FLAG_REQUIRE_STAGING) > 0, +// (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0, +// (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0, +// state, +// proto.getBytes(), +// 0, +// 0, +// false); + return true; + } + + /** + * Write an event to stats log using the raw format. + * + * @param buffer The encoded buffer of data to write. + * @param size The number of bytes from the buffer to write. + * @hide + * @deprecated Use {@link write(final StatsEvent statsEvent)} instead. + * + */ + @Deprecated + @SystemApi + public static void writeRaw(@NonNull byte[] buffer, int size) { + writeImpl(buffer, size, 0); + } + + /** + * Write an event to stats log using the raw format. + * + * @param buffer The encoded buffer of data to write. + * @param size The number of bytes from the buffer to write. + * @param atomId The id of the atom to which the event belongs. + */ +// private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId); + private static void writeImpl(@NonNull byte[] buffer, int size, int atomId) { + // no-op for now + } + + /** + * Write an event to stats log using the raw format encapsulated in StatsEvent. + * After writing to stats log, release() is called on the StatsEvent object. + * No further action should be taken on the StatsEvent object following this call. + * + * @param statsEvent The StatsEvent object containing the encoded buffer of data to write. + * @hide + */ + @SystemApi + public static void write(@NonNull final StatsEvent statsEvent) { + writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId()); + statsEvent.release(); + } +} diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java b/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java new file mode 100644 index 000000000000..c7376842d8f3 --- /dev/null +++ b/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2019 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 android.compat; + +// [Ravenwood] Copied from libcore, with "RAVENWOOD-CHANGE" + +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + +import android.annotation.SystemApi; +import android.compat.annotation.ChangeId; + +import libcore.api.IntraCoreApi; +import libcore.util.NonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Internal APIs for logging and gating compatibility changes. + * + * @see ChangeId + * + * @hide + */ +@SystemApi(client = MODULE_LIBRARIES) +@IntraCoreApi +public final class Compatibility { + + private Compatibility() {} + + /** + * Reports that a compatibility change is affecting the current process now. + * + * <p>Calls to this method from a non-app process are ignored. This allows code implementing + * APIs that are used by apps and by other code (e.g. the system server) to report changes + * regardless of the process it's running in. When called in a non-app process, this method is + * a no-op. + * + * <p>Note: for changes that are gated using {@link #isChangeEnabled(long)}, you do not need to + * call this API directly. The change will be reported for you in the case that + * {@link #isChangeEnabled(long)} returns {@code true}. + * + * @param changeId The ID of the compatibility change taking effect. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public static void reportUnconditionalChange(@ChangeId long changeId) { + sCallbacks.onChangeReported(changeId); + } + + /** + * Query if a given compatibility change is enabled for the current process. This method should + * only be called by code running inside a process of the affected app. + * + * <p>If this method returns {@code true}, the calling code should implement the compatibility + * change, resulting in differing behaviour compared to earlier releases. If this method returns + * {@code false}, the calling code should behave as it did in earlier releases. + * + * <p>When this method returns {@code true}, it will also report the change as + * {@link #reportUnconditionalChange(long)} would, so there is no need to call that method + * directly. + * + * @param changeId The ID of the compatibility change in question. + * @return {@code true} if the change is enabled for the current app. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public static boolean isChangeEnabled(@ChangeId long changeId) { + return sCallbacks.isChangeEnabled(changeId); + } + + private static final BehaviorChangeDelegate DEFAULT_CALLBACKS = new BehaviorChangeDelegate(){}; + + private volatile static BehaviorChangeDelegate sCallbacks = DEFAULT_CALLBACKS; + + /** + * Sets the behavior change delegate. + * + * All changes reported via the {@link Compatibility} class will be forwarded to this class. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static void setBehaviorChangeDelegate(BehaviorChangeDelegate callbacks) { + sCallbacks = Objects.requireNonNull(callbacks); + } + + /** + * Removes a behavior change delegate previously set via {@link #setBehaviorChangeDelegate}. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static void clearBehaviorChangeDelegate() { + sCallbacks = DEFAULT_CALLBACKS; + } + + /** + * Return the behavior change delegate + * + * @hide + */ + // VisibleForTesting + @NonNull + public static BehaviorChangeDelegate getBehaviorChangeDelegate() { + return sCallbacks; + } + + /** + * For use by tests only. Causes values from {@code overrides} to be returned instead of the + * real value. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static void setOverrides(ChangeConfig overrides) { + // Setting overrides twice in a row does not need to be supported because + // this method is only for enabling/disabling changes for the duration of + // a single test. + // In production, the app is restarted when changes get enabled or disabled, + // and the ChangeConfig is then set exactly once on that app process. + if (sCallbacks instanceof OverrideCallbacks) { + throw new IllegalStateException("setOverrides has already been called!"); + } + sCallbacks = new OverrideCallbacks(sCallbacks, overrides); + } + + /** + * For use by tests only. Removes overrides set by {@link #setOverrides}. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static void clearOverrides() { + if (!(sCallbacks instanceof OverrideCallbacks)) { + throw new IllegalStateException("No overrides set"); + } + sCallbacks = ((OverrideCallbacks) sCallbacks).delegate; + } + + /** + * Base class for compatibility API implementations. The default implementation logs a warning + * to logcat. + * + * This is provided as a class rather than an interface to allow new methods to be added without + * breaking @SystemApi binary compatibility. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public interface BehaviorChangeDelegate { + /** + * Called when a change is reported via {@link Compatibility#reportUnconditionalChange} + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + default void onChangeReported(long changeId) { + // Do not use String.format here (b/160912695) + + // RAVENWOOD-CHANGE + System.out.println("No Compatibility callbacks set! Reporting change " + changeId); + } + + /** + * Called when a change is queried via {@link Compatibility#isChangeEnabled} + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + default boolean isChangeEnabled(long changeId) { + // Do not use String.format here (b/160912695) + // TODO(b/289900411): Rate limit this log if it's necessary in the release build. + // System.logW("No Compatibility callbacks set! Querying change " + changeId); + return true; + } + } + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public static final class ChangeConfig { + private final Set<Long> enabled; + private final Set<Long> disabled; + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public ChangeConfig(@NonNull Set<@NonNull Long> enabled, @NonNull Set<@NonNull Long> disabled) { + this.enabled = Objects.requireNonNull(enabled); + this.disabled = Objects.requireNonNull(disabled); + if (enabled.contains(null)) { + throw new NullPointerException(); + } + if (disabled.contains(null)) { + throw new NullPointerException(); + } + Set<Long> intersection = new HashSet<>(enabled); + intersection.retainAll(disabled); + if (!intersection.isEmpty()) { + throw new IllegalArgumentException("Cannot have changes " + intersection + + " enabled and disabled!"); + } + } + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public boolean isEmpty() { + return enabled.isEmpty() && disabled.isEmpty(); + } + + private static long[] toLongArray(Set<Long> values) { + long[] result = new long[values.size()]; + int idx = 0; + for (Long value: values) { + result[idx++] = value; + } + return result; + } + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public @NonNull long[] getEnabledChangesArray() { + return toLongArray(enabled); + } + + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public @NonNull long[] getDisabledChangesArray() { + return toLongArray(disabled); + } + + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public @NonNull Set<@NonNull Long> getEnabledSet() { + return Collections.unmodifiableSet(enabled); + } + + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public @NonNull Set<@NonNull Long> getDisabledSet() { + return Collections.unmodifiableSet(disabled); + } + + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public boolean isForceEnabled(long changeId) { + return enabled.contains(changeId); + } + + + /** + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @IntraCoreApi + public boolean isForceDisabled(long changeId) { + return disabled.contains(changeId); + } + + + /** + * @hide + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ChangeConfig)) { + return false; + } + ChangeConfig that = (ChangeConfig) o; + return enabled.equals(that.enabled) && + disabled.equals(that.disabled); + } + + /** + * @hide + */ + @Override + public int hashCode() { + return Objects.hash(enabled, disabled); + } + + + /** + * @hide + */ + @Override + public String toString() { + return "ChangeConfig{enabled=" + enabled + ", disabled=" + disabled + '}'; + } + } + + private static class OverrideCallbacks implements BehaviorChangeDelegate { + private final BehaviorChangeDelegate delegate; + private final ChangeConfig changeConfig; + + private OverrideCallbacks(BehaviorChangeDelegate delegate, ChangeConfig changeConfig) { + this.delegate = Objects.requireNonNull(delegate); + this.changeConfig = Objects.requireNonNull(changeConfig); + } + @Override + public boolean isChangeEnabled(long changeId) { + if (changeConfig.isForceEnabled(changeId)) { + return true; + } + if (changeConfig.isForceDisabled(changeId)) { + return false; + } + return delegate.isChangeEnabled(changeId); + } + } +} diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java new file mode 100644 index 000000000000..00730efc64b3 --- /dev/null +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java @@ -0,0 +1,69 @@ + +/* + * Copyright (C) 2018 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 libcore.api; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates an API is part of a contract provided by the "core" set of + * libraries to select parts of the Android software stack. + * + * <p>This annotation should only appear on either (a) classes that are hidden by <pre>@hide</pre> + * javadoc tags or equivalent annotations, or (b) members of such classes. It is for use with + * metalava's {@code --show-single-annotation} option and so must be applied at the class level and + * applied again each member that is to be made part of the API. Members that are not part of the + * API do not have to be explicitly hidden. + * + * @hide + */ +@IntraCoreApi +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE}) +@Retention(RetentionPolicy.SOURCE) +public @interface CorePlatformApi { + + /** Enumeration of the possible statuses of the API in the core/platform API surface. */ + @IntraCoreApi + enum Status { + + /** + * This API is considered stable, and so present in both the stable and legacy version of + * the API surface. + */ + @IntraCoreApi + STABLE, + + /** + * This API is not (yet) considered stable, and so only present in the legacy version of + * the API surface. + */ + @IntraCoreApi + LEGACY_ONLY + } + + /** The status of the API in the core/platform API surface. */ + @IntraCoreApi + Status status() default Status.LEGACY_ONLY; +} diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java new file mode 100644 index 000000000000..f87ff11df0ab --- /dev/null +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 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 libcore.api; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that an API is hidden by default, in a similar fashion to the + * <pre>@hide</pre> javadoc tag. + * + * <p>Note that, in order for this to work, metalava has to be invoked with + * the flag {@code --hide-annotation libcore.api.Hide}. + * + * <p>This annotation should be used in {@code .annotated.java} stub files which + * contain API inclusion information about {@code libcore/ojluni} classes, to + * avoid patching the source files with <pre>@hide</pre> javadoc tags. All + * build targets which consume these stub files should also apply the above + * metalava flag. + * + * @hide + */ +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE}) +@Retention(RetentionPolicy.SOURCE) +public @interface Hide { +} diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java new file mode 100644 index 000000000000..87cfcff2474b --- /dev/null +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 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 libcore.api; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates an API is part of a contract within the "core" set of libraries, some of which may + * be mmodules. + * + * <p>This annotation should only appear on either (a) classes that are hidden by <pre>@hide</pre> + * javadoc tags or equivalent annotations, or (b) members of such classes. It is for use with + * metalava's {@code --show-single-annotation} option and so must be applied at the class level and + * applied again each member that is to be made part of the API. Members that are not part of the + * API do not have to be explicitly hidden. + * + * @hide + */ +@IntraCoreApi // @IntraCoreApi is itself part of the intra-core API +@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE}) +@Retention(RetentionPolicy.SOURCE) +public @interface IntraCoreApi { +} diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java new file mode 100644 index 000000000000..db3cd8ed712f --- /dev/null +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java @@ -0,0 +1,48 @@ +/* + * 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. + */ +package libcore.util; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that a type use can never be null. + * <p> + * This is a marker annotation and it has no specific attributes. + * @hide + */ +@Documented +@Retention(SOURCE) +@Target({FIELD, METHOD, PARAMETER, TYPE_USE}) +@libcore.api.IntraCoreApi +public @interface NonNull { + /** + * Min Android API level (inclusive) to which this annotation is applied. + */ + int from() default Integer.MIN_VALUE; + + /** + * Max Android API level to which this annotation is applied. + */ + int to() default Integer.MAX_VALUE; +} diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java new file mode 100644 index 000000000000..3371978b0568 --- /dev/null +++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java @@ -0,0 +1,48 @@ +/* + * 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. + */ +package libcore.util; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that a type use can be a null. + * <p> + * This is a marker annotation and it has no specific attributes. + * @hide + */ +@Documented +@Retention(SOURCE) +@Target({FIELD, METHOD, PARAMETER, TYPE_USE}) +@libcore.api.IntraCoreApi +public @interface Nullable { + /** + * Min Android API level (inclusive) to which this annotation is applied. + */ + int from() default Integer.MIN_VALUE; + + /** + * Max Android API level to which this annotation is applied. + */ + int to() default Integer.MAX_VALUE; +} diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt index 3649f0e78f09..b64944ee39ed 100644 --- a/ravenwood/texts/ravenwood-framework-policies.txt +++ b/ravenwood/texts/ravenwood-framework-policies.txt @@ -5,6 +5,10 @@ rename com/.*/nano/ devicenano/ rename android/.*/nano/ devicenano/ + +# StatsD autogenerated classes. Maybe add a heuristic? +class com.android.internal.util.FrameworkStatsLog keepclass + # Exported to Mainline modules; cannot use annotations class com.android.internal.util.FastXmlSerializer keepclass class com.android.internal.util.FileRotator keepclass diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index a77ba624a68f..ce1a292fb069 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -64,6 +64,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.Flags; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import java.util.ArrayList; @@ -396,7 +397,7 @@ public class FullScreenMagnificationController implements mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) { sendSpecToAnimation(mCurrentMagnificationSpec, null); } - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } magnified.recycle(); } @@ -474,8 +475,16 @@ public class FullScreenMagnificationController implements return mIdOfLastServiceToMagnify; } + /** + * This is invoked whenever magnification change happens. + * + * @param isScaleTransient represents that if the scale is being changed and the changed + * value may be short lived and be updated again soon. + * Calling the method usually notifies input manager to update the + * cursor scale, but setting this value {@code true} prevents it. + */ @GuardedBy("mLock") - void onMagnificationChangedLocked() { + void onMagnificationChangedLocked(boolean isScaleTransient) { final float scale = getScale(); final float centerX = getCenterX(); final float centerY = getCenterY(); @@ -498,6 +507,10 @@ public class FullScreenMagnificationController implements } else { hideThumbnail(); } + + if (!isScaleTransient) { + notifyScaleForInput(mDisplayId, scale); + } } @GuardedBy("mLock") @@ -611,8 +624,9 @@ public class FullScreenMagnificationController implements * Directly Zooms out the scale to 1f with animating the transition. This method is * triggered only by service automatically, such as when user context changed. */ + @GuardedBy("mLock") void zoomOutFromService() { - setScaleAndCenter(1.0f, Float.NaN, Float.NaN, + setScaleAndCenter(1.0f, Float.NaN, Float.NaN, /* isScaleTransient= */ false, transformToStubCallback(true), AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mZoomedOutFromService = true; @@ -640,7 +654,7 @@ public class FullScreenMagnificationController implements setActivated(false); if (changed) { spec.clear(); - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } mIdOfLastServiceToMagnify = INVALID_SERVICE_ID; sendSpecToAnimation(spec, animationCallback); @@ -651,7 +665,7 @@ public class FullScreenMagnificationController implements } @GuardedBy("mLock") - boolean setScale(float scale, float pivotX, float pivotY, + boolean setScale(float scale, float pivotX, float pivotY, boolean isScaleTransient, boolean animate, int id) { if (!mRegistered) { return false; @@ -674,12 +688,14 @@ public class FullScreenMagnificationController implements final float centerX = normPivotX + offsetX; final float centerY = normPivotY + offsetY; mIdOfLastServiceToMagnify = id; - return setScaleAndCenter(scale, centerX, centerY, transformToStubCallback(animate), id); + return setScaleAndCenter(scale, centerX, centerY, isScaleTransient, + transformToStubCallback(animate), id); } @GuardedBy("mLock") boolean setScaleAndCenter(float scale, float centerX, float centerY, - MagnificationAnimationCallback animationCallback, int id) { + boolean isScaleTransient, MagnificationAnimationCallback animationCallback, + int id) { if (!mRegistered) { return false; } @@ -696,7 +712,7 @@ public class FullScreenMagnificationController implements + animationCallback + ", id = " + id + ")"); } boolean changed = setActivated(true); - changed |= updateMagnificationSpecLocked(scale, centerX, centerY); + changed |= updateMagnificationSpecLocked(scale, centerX, centerY, isScaleTransient); sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback); if (isActivated() && (id != INVALID_SERVICE_ID)) { mIdOfLastServiceToMagnify = id; @@ -773,7 +789,9 @@ public class FullScreenMagnificationController implements * @return {@code true} if the magnification spec changed or {@code false} * otherwise */ - boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) { + @GuardedBy("mLock") + boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY, + boolean isScaleTransient) { // Handle defaults. if (Float.isNaN(centerX)) { centerX = getCenterX(); @@ -801,7 +819,7 @@ public class FullScreenMagnificationController implements changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY); if (changed) { - onMagnificationChangedLocked(); + onMagnificationChangedLocked(isScaleTransient); } return changed; @@ -816,7 +834,7 @@ public class FullScreenMagnificationController implements final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX; final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY; if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) { - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } if (id != INVALID_SERVICE_ID) { mIdOfLastServiceToMagnify = id; @@ -861,7 +879,7 @@ public class FullScreenMagnificationController implements } synchronized (mLock) { mCurrentMagnificationSpec.setTo(lastSpecSent); - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } } }); @@ -955,6 +973,7 @@ public class FullScreenMagnificationController implements context, traceManager, LocalServices.getService(WindowManagerInternal.class), + LocalServices.getService(InputManagerInternal.class), new Handler(context.getMainLooper()), context.getResources().getInteger(R.integer.config_longAnimTime)), lock, @@ -1464,20 +1483,24 @@ public class FullScreenMagnificationController implements * @param scale the target scale, must be >= 1 * @param pivotX the screen-relative X coordinate around which to scale * @param pivotY the screen-relative Y coordinate around which to scale + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. * @param animate {@code true} to animate the transition, {@code false} * to transition immediately * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setScale(int displayId, float scale, float pivotX, float pivotY, - boolean animate, int id) { + boolean isScaleTransient, boolean animate, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } - return display.setScale(scale, pivotX, pivotY, animate, id); + return display.setScale(scale, pivotX, pivotY, isScaleTransient, animate, id); } } @@ -1496,6 +1519,8 @@ public class FullScreenMagnificationController implements * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); @@ -1503,7 +1528,7 @@ public class FullScreenMagnificationController implements return false; } return display.setScaleAndCenter(Float.NaN, centerX, centerY, - animate ? STUB_ANIMATION_CALLBACK : null, id); + /* isScaleTransient= */ false, animate ? STUB_ANIMATION_CALLBACK : null, id); } } @@ -1526,7 +1551,32 @@ public class FullScreenMagnificationController implements */ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate, int id) { - return setScaleAndCenter(displayId, scale, centerX, centerY, + return setScaleAndCenter(displayId, scale, centerX, centerY, /* isScaleTransient= */ false, + transformToStubCallback(animate), id); + } + + /** + * Sets the scale and center of the magnified region, optionally + * animating the transition. If animation is disabled, the transition + * is immediate. + * + * @param displayId The logical display id. + * @param scale the target scale, or {@link Float#NaN} to leave unchanged + * @param centerX the screen-relative X coordinate around which to + * center and scale, or {@link Float#NaN} to leave unchanged + * @param centerY the screen-relative Y coordinate around which to + * center and scale, or {@link Float#NaN} to leave unchanged + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. + * @param animate {@code true} to animate the transition, {@code false} + * to transition immediately + * @param id the ID of the service requesting the change + * @return {@code true} if the magnification spec changed, {@code false} if + * the spec did not change + */ + public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, + boolean isScaleTransient, boolean animate, int id) { + return setScaleAndCenter(displayId, scale, centerX, centerY, isScaleTransient, transformToStubCallback(animate), id); } @@ -1541,20 +1591,25 @@ public class FullScreenMagnificationController implements * center and scale, or {@link Float#NaN} to leave unchanged * @param centerY the screen-relative Y coordinate around which to * center and scale, or {@link Float#NaN} to leave unchanged + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. * @param animationCallback Called when the animation result is valid. * {@code null} to transition immediately * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, - MagnificationAnimationCallback animationCallback, int id) { + boolean isScaleTransient, MagnificationAnimationCallback animationCallback, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } - return display.setScaleAndCenter(scale, centerX, centerY, animationCallback, id); + return display.setScaleAndCenter(scale, centerX, centerY, isScaleTransient, + animationCallback, id); } } @@ -1569,6 +1624,8 @@ public class FullScreenMagnificationController implements * screen pixels. * @param id the ID of the service requesting the change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); @@ -1640,6 +1697,8 @@ public class FullScreenMagnificationController implements */ public void persistScale(int displayId) { final float scale = getScale(displayId); + notifyScaleForInput(displayId, scale); + if (scale < MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) { return; } @@ -1665,6 +1724,8 @@ public class FullScreenMagnificationController implements * * @param displayId The logical display id. */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. private void zoomOutFromService(int displayId) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); @@ -1691,6 +1752,20 @@ public class FullScreenMagnificationController implements } /** + * Notifies input manager that magnification scale changed non-transiently + * so that pointer cursor is scaled as well. + * + * @param displayId The logical display id. + * @param scale The new scale factor. + */ + public void notifyScaleForInput(int displayId, float scale) { + if (Flags.magnificationEnlargePointer()) { + mControllerCtx.getInputManager() + .setAccessibilityPointerIconScaleFactor(displayId, scale); + } + } + + /** * Resets all displays' magnification if last magnifying service is disabled. * * @param connectionId @@ -2166,6 +2241,7 @@ public class FullScreenMagnificationController implements private final Context mContext; private final AccessibilityTraceManager mTrace; private final WindowManagerInternal mWindowManager; + private final InputManagerInternal mInputManager; private final Handler mHandler; private final Long mAnimationDuration; @@ -2175,11 +2251,13 @@ public class FullScreenMagnificationController implements public ControllerContext(@NonNull Context context, @NonNull AccessibilityTraceManager traceManager, @NonNull WindowManagerInternal windowManager, + @NonNull InputManagerInternal inputManager, @NonNull Handler handler, long animationDuration) { mContext = context; mTrace = traceManager; mWindowManager = windowManager; + mInputManager = inputManager; mHandler = handler; mAnimationDuration = animationDuration; } @@ -2209,6 +2287,14 @@ public class FullScreenMagnificationController implements } /** + * @return InputManagerInternal + */ + @NonNull + public InputManagerInternal getInputManager() { + return mInputManager; + } + + /** * @return Handler for main looper */ @NonNull diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index 963334b07ea6..c6a966f47952 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -617,7 +617,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x"); - mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false, + mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, + /* isScaleTransient= */ true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); checkShouldDetectPassPersistedScale(); @@ -1974,6 +1975,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH /* scale= */ scale, /* centerX= */ mPivotEdge.x, /* centerY= */ mPivotEdge.y, + /* isScaleTransient= */ true, /* animate= */ true, /* id= */ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); if (scale == 1.0f) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 1489d16c3764..d40e7476f7ec 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -176,7 +176,8 @@ public class MagnificationController implements MagnificationConnectionManager.C public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) { if (getFullScreenMagnificationController().isActivated(displayId)) { getFullScreenMagnificationController().setScaleAndCenter(displayId, scale, - Float.NaN, Float.NaN, false, MAGNIFICATION_GESTURE_HANDLER_ID); + Float.NaN, Float.NaN, /* isScaleTransient= */ !updatePersistence, false, + MAGNIFICATION_GESTURE_HANDLER_ID); if (updatePersistence) { getFullScreenMagnificationController().persistScale(displayId); } @@ -371,7 +372,7 @@ public class MagnificationController implements MagnificationConnectionManager.C } screenMagnificationController.setScaleAndCenter(displayId, targetScale, magnificationCenter.x, magnificationCenter.y, - magnificationAnimationCallback, id); + /* isScaleTransient= */ false, magnificationAnimationCallback, id); } else { if (screenMagnificationController.isRegistered(displayId)) { screenMagnificationController.reset(displayId, false); diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 95281c81fc33..591107010431 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -715,11 +715,17 @@ public class CompanionDeviceManagerService extends SystemService { @Override public byte[] getBackupPayload(int userId) { + if (getCallingUid() != SYSTEM_UID) { + throw new SecurityException("Caller must be system"); + } return mBackupRestoreProcessor.getBackupPayload(userId); } @Override public void applyRestoredPayload(byte[] payload, int userId) { + if (getCallingUid() != SYSTEM_UID) { + throw new SecurityException("Caller must be system"); + } mBackupRestoreProcessor.applyRestoredPayload(payload, userId); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 39ac5150c7f1..363807d2aa8c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -68,6 +68,7 @@ import android.telephony.CellSignalStrengthWcdma; import android.telephony.DisconnectCause; import android.telephony.LinkCapacityEstimate; import android.telephony.LocationAccessPolicy; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneCapability; import android.telephony.PhoneStateListener; import android.telephony.PhysicalChannelConfig; @@ -90,6 +91,7 @@ import android.telephony.ims.MediaQualityStatus; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.LocalLog; import android.util.Pair; import android.util.SparseArray; @@ -429,6 +431,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean[] mCarrierRoamingNtnMode = null; private boolean[] mCarrierRoamingNtnEligible = null; + private List<IntArray> mCarrierRoamingNtnAvailableServices; + /** * Per-phone map of precise data connection state. The key of the map is the pair of transport * type and APN setting. This is the cache to prevent redundant callbacks to the listeners. @@ -741,6 +745,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { cutListToSize(mCarrierServiceStates, mNumPhones); cutListToSize(mCallStateLists, mNumPhones); cutListToSize(mMediaQualityStatus, mNumPhones); + cutListToSize(mCarrierRoamingNtnAvailableServices, mNumPhones); return; } @@ -789,6 +794,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSCBMDuration[i] = 0; mCarrierRoamingNtnMode[i] = false; mCarrierRoamingNtnEligible[i] = false; + mCarrierRoamingNtnAvailableServices.add(i, new IntArray()); } } } @@ -864,6 +870,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSCBMDuration = new long[numPhones]; mCarrierRoamingNtnMode = new boolean[numPhones]; mCarrierRoamingNtnEligible = new boolean[numPhones]; + mCarrierRoamingNtnAvailableServices = new ArrayList<>(); for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; @@ -909,6 +916,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSCBMDuration[i] = 0; mCarrierRoamingNtnMode[i] = false; mCarrierRoamingNtnEligible[i] = false; + mCarrierRoamingNtnAvailableServices.add(i, new IntArray()); } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -1533,6 +1541,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if (events.contains( + TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED)) { + try { + r.callback.onCarrierRoamingNtnAvailableServicesChanged( + mCarrierRoamingNtnAvailableServices.get(r.phoneId).toArray()); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } @@ -3642,6 +3659,47 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Notify external listeners that carrier roaming non-terrestrial available services changed. + * @param availableServices The list of the supported services. + */ + public void notifyCarrierRoamingNtnAvailableServicesChanged( + int subId, @NetworkRegistrationInfo.ServiceType int[] availableServices) { + if (!checkNotifyPermission("notifyCarrierRoamingNtnEligibleStateChanged")) { + log("notifyCarrierRoamingNtnAvailableServicesChanged: caller does not have required " + + "permissions."); + return; + } + + if (VDBG) { + log("notifyCarrierRoamingNtnAvailableServicesChanged: " + + "availableServices=" + Arrays.toString(availableServices)); + } + + synchronized (mRecords) { + int phoneId = getPhoneIdFromSubId(subId); + if (!validatePhoneId(phoneId)) { + loge("Invalid phone ID " + phoneId + " for " + subId); + return; + } + IntArray availableServicesIntArray = new IntArray(availableServices.length); + availableServicesIntArray.addAll(availableServices); + mCarrierRoamingNtnAvailableServices.set(phoneId, availableServicesIntArray); + for (Record r : mRecords) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED) + && idMatch(r, subId, phoneId)) { + try { + r.callback.onCarrierRoamingNtnAvailableServicesChanged(availableServices); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + @NeverCompile // Avoid size overhead of debugging code. @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @@ -3706,6 +3764,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { Pair<String, Integer> carrierServiceState = mCarrierServiceStates.get(i); pw.println("mCarrierServiceState=<package=" + pii(carrierServiceState.first) + ", uid=" + carrierServiceState.second + ">"); + pw.println("mCarrierRoamingNtnAvailableServices=" + + mCarrierRoamingNtnAvailableServices.get(i)); pw.decreaseIndent(); } diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 947f6b73d32a..51c768b80eff 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -75,6 +75,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.Log; import android.util.Slog; @@ -82,7 +83,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.internal.util.IndentingPrintWriter; +import com.android.net.module.util.BinderUtils; import com.android.net.module.util.LocationPermissionChecker; import com.android.net.module.util.PermissionUtils; import com.android.server.vcn.TelephonySubscriptionTracker; @@ -448,7 +449,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { final UserHandle userHandle = UserHandle.getUserHandleForUid(uid); final UserManager userManager = mContext.getSystemService(UserManager.class); - Binder.withCleanCallingIdentity( + BinderUtils.withCleanCallingIdentity( () -> { if (!Objects.equals(userManager.getMainUser(), userHandle)) { throw new SecurityException( @@ -468,7 +469,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO (b/172619301): Check based on events propagated from CarrierPrivilegesTracker final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class); final List<SubscriptionInfo> subscriptionInfos = new ArrayList<>(); - Binder.withCleanCallingIdentity( + BinderUtils.withCleanCallingIdentity( () -> { List<SubscriptionInfo> subsInGroup = subMgr.getSubscriptionsInGroup(subscriptionGroup); @@ -700,7 +701,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void notifyAllPolicyListenersLocked() { for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) { - Binder.withCleanCallingIdentity(() -> { + BinderUtils.withCleanCallingIdentity(() -> { try { policyListener.mListener.onPolicyChanged(); } catch (RemoteException e) { @@ -715,7 +716,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull ParcelUuid subGroup, @VcnStatusCode int statusCode) { for (final VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (isCallbackPermissioned(cbInfo, subGroup)) { - Binder.withCleanCallingIdentity(() -> { + BinderUtils.withCleanCallingIdentity(() -> { try { cbInfo.mCallback.onVcnStatusChanged(statusCode); } catch (RemoteException e) { @@ -795,7 +796,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { enforceManageTestNetworksForTestMode(config); enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName); - Binder.withCleanCallingIdentity(() -> { + BinderUtils.withCleanCallingIdentity(() -> { synchronized (mLock) { mConfigs.put(subscriptionGroup, config); startOrUpdateVcnLocked(subscriptionGroup, config); @@ -853,7 +854,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { .checkPackage(mDeps.getBinderCallingUid(), opPkgName); enforceCarrierPrivilegeOrProvisioningPackage(subscriptionGroup, opPkgName); - Binder.withCleanCallingIdentity(() -> { + BinderUtils.withCleanCallingIdentity(() -> { synchronized (mLock) { stopAndClearVcnConfigInternalLocked(subscriptionGroup); writeConfigsToDiskLocked(); @@ -991,7 +992,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { android.Manifest.permission.NETWORK_FACTORY, android.Manifest.permission.MANAGE_TEST_NETWORKS); - Binder.withCleanCallingIdentity(() -> { + BinderUtils.withCleanCallingIdentity(() -> { PolicyListenerBinderDeath listenerBinderDeath = new PolicyListenerBinderDeath(listener); synchronized (mLock) { @@ -1018,7 +1019,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { android.Manifest.permission.NETWORK_FACTORY, android.Manifest.permission.MANAGE_TEST_NETWORKS); - Binder.withCleanCallingIdentity(() -> { + BinderUtils.withCleanCallingIdentity(() -> { synchronized (mLock) { PolicyListenerBinderDeath listenerBinderDeath = mRegisteredPolicyListeners.remove(listener.asBinder()); @@ -1082,7 +1083,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { + " MANAGE_TEST_NETWORKS"); } - return Binder.withCleanCallingIdentity(() -> { + return BinderUtils.withCleanCallingIdentity(() -> { // Defensive copy in case this call is in-process and the given NetworkCapabilities // mutates final NetworkCapabilities ncCopy = new NetworkCapabilities(networkCapabilities); @@ -1521,7 +1522,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { // Notify all registered StatusCallbacks for this subGroup for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (isCallbackPermissioned(cbInfo, mSubGroup)) { - Binder.withCleanCallingIdentity(() -> { + BinderUtils.withCleanCallingIdentity(() -> { try { cbInfo.mCallback.onGatewayConnectionError( gatewayConnectionName, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 217ef205a3e8..6ba851423219 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -60,6 +60,7 @@ import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BACKUP; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_INSTRUMENTATION; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM; +import static android.content.Intent.isPreventIntentRedirectEnabled; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES; import static android.content.pm.PackageManager.MATCH_ALL; @@ -130,7 +131,6 @@ import static android.os.Process.setThreadScheduler; import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; import static android.provider.Settings.Global.DEBUG_APP; import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; -import static android.security.Flags.preventIntentRedirect; import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS; import static android.view.Display.INVALID_DISPLAY; @@ -19272,7 +19272,7 @@ public class ActivityManagerService extends IActivityManager.Stub * @hide */ public void addCreatorToken(@Nullable Intent intent, String creatorPackage) { - if (!preventIntentRedirect()) return; + if (!isPreventIntentRedirectEnabled()) return; if (intent == null || intent.getExtraIntentKeys() == null) return; for (String key : intent.getExtraIntentKeys()) { @@ -19283,7 +19283,9 @@ public class ActivityManagerService extends IActivityManager.Stub + "} does not correspond to an intent in the extra bundle."); continue; } - Slog.wtf(TAG, "A creator token is added to an intent."); + Slog.wtf(TAG, + "A creator token is added to an intent. creatorPackage: " + creatorPackage + + "; intent: " + intent); IBinder creatorToken = createIntentCreatorToken(extraIntent, creatorPackage); if (creatorToken != null) { extraIntent.setCreatorToken(creatorToken); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index c47cad955202..28b606c931fa 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -3178,12 +3178,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.collectPowerStatsSamples(); } - BatteryUsageStats batteryUsageStats = - mBatteryUsageStatsProvider.getBatteryUsageStats(mStats, query); - if (proto) { - batteryUsageStats.dumpToProto(fd); - } else { - batteryUsageStats.dump(pw, " "); + try (BatteryUsageStats batteryUsageStats = + mBatteryUsageStatsProvider.getBatteryUsageStats(mStats, query)) { + if (proto) { + batteryUsageStats.dumpToProto(fd); + } else { + batteryUsageStats.dump(pw, " "); + } + } catch (IOException e) { + Slog.e(TAG, "Cannot close BatteryUsageStats", e); } } diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index ba4b71cd7540..17fcbf47206f 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -22,7 +22,7 @@ import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR; import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.am.ProcessRecord.TAG; -import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; +import static com.android.internal.os.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; import android.annotation.Nullable; import android.app.ActivityManager; @@ -58,7 +58,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.modules.expresslog.Counter; import com.android.server.ResourcePressureUtil; import com.android.server.criticalevents.CriticalEventLog; -import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot; +import com.android.internal.os.ProcfsMemoryUtil.MemorySnapshot; import com.android.server.wm.WindowProcessController; import java.io.File; diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java index 1d68ee54d96c..83b0801ce87f 100644 --- a/services/core/java/com/android/server/display/BrightnessRangeController.java +++ b/services/core/java/com/android/server/display/BrightnessRangeController.java @@ -67,6 +67,10 @@ class BrightnessRangeController { mNormalBrightnessModeController.resetNbmData( displayDeviceConfig.getLuxThrottlingData()); } + if (flags.useNewHdrBrightnessModifier()) { + // HDR boost is handled by HdrBrightnessModifier and should be disabled in HbmController + mHbmController.disableHdrBoost(); + } updateHdrClamper(info, displayToken, displayDeviceConfig); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 42a62f098b6a..5b61f006def6 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1503,7 +1503,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // use the current brightness setting scaled by the doze scale factor rawBrightnessState = getDozeBrightnessForOffload(); brightnessState = clampScreenBrightness(rawBrightnessState); - updateScreenBrightnessSetting = false; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_MANUAL); mTempBrightnessEvent.setFlags( mTempBrightnessEvent.getFlags() | BrightnessEvent.FLAG_DOZE_SCALE); @@ -1513,6 +1512,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightnessState = clampScreenBrightness(rawBrightnessState); mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); } + updateScreenBrightnessSetting = false; } if (!mFlags.isRefactorDisplayPowerControllerEnabled()) { diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 135cab6d0614..6be0c123d262 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -38,6 +38,7 @@ import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.util.FrameworkStatsLog; import com.android.server.display.DisplayManagerService.Clock; import com.android.server.display.config.HighBrightnessModeData; +import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.utils.DebugUtils; import java.io.PrintWriter; @@ -119,6 +120,14 @@ class HighBrightnessModeController { @Nullable private HighBrightnessModeMetadata mHighBrightnessModeMetadata; + /** + * If {@link DisplayManagerFlags#useNewHdrBrightnessModifier()} is ON, hdr boost is handled by + * {@link com.android.server.display.brightness.clamper.HdrBrightnessModifier} and should be + * disabled in this class. After flag is cleaned up, this field together with HDR handling + * should be cleaned up from this class. + */ + private boolean mHdrBoostDisabled = false; + HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg, @@ -323,6 +332,7 @@ class HighBrightnessModeController { pw.println(" mIsTimeAvailable= " + mIsTimeAvailable); pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode); pw.println(" width*height=" + mWidth + "*" + mHeight); + pw.println(" mHdrBoostDisabled=" + mHdrBoostDisabled); if (mHighBrightnessModeMetadata != null) { pw.println(" mRunningStartTimeMillis=" @@ -373,6 +383,11 @@ class HighBrightnessModeController { return mHbmData != null && mHighBrightnessModeMetadata != null; } + void disableHdrBoost() { + mHdrBoostDisabled = true; + unregisterHdrListener(); + } + private long calculateRemainingTime(long currentTime) { if (!deviceSupportsHbm()) { return 0; @@ -583,6 +598,9 @@ class HighBrightnessModeController { } private void registerHdrListener(IBinder displayToken) { + if (mHdrBoostDisabled) { + return; + } if (mRegisteredDisplayToken == displayToken) { return; } diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index 99f7f12567b4..c888eef7f5df 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -262,4 +262,12 @@ public abstract class InputManagerInternal { */ public abstract void handleKeyGestureInKeyGestureController(int deviceId, int[] keycodes, int modifierState, @KeyGestureEvent.KeyGestureType int event); + + /** + * Sets the magnification scale factor for pointer icons. + * + * @param displayId the ID of the display where the new scale factor is applied. + * @param scaleFactor the new scale factor to be applied for pointer icons. + */ + public abstract void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 8acf583e0765..98e5319cde30 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3506,6 +3506,11 @@ public class InputManagerService extends IInputManager.Stub int modifierState, @KeyGestureEvent.KeyGestureType int gestureType) { mKeyGestureController.handleKeyGesture(deviceId, keycodes, modifierState, gestureType); } + + @Override + public void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) { + InputManagerService.this.setAccessibilityPointerIconScaleFactor(displayId, scaleFactor); + } } @Override @@ -3688,6 +3693,10 @@ public class InputManagerService extends IInputManager.Stub mPointerIconCache.setPointerScale(scale); } + void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) { + mPointerIconCache.setAccessibilityScaleFactor(displayId, scaleFactor); + } + interface KeyboardBacklightControllerInterface { default void incrementKeyboardBacklight(int deviceId) {} default void decrementKeyboardBacklight(int deviceId) {} diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java index b488db533d12..2f5236f51c48 100644 --- a/services/core/java/com/android/server/input/KeyGestureController.java +++ b/services/core/java/com/android/server/input/KeyGestureController.java @@ -23,6 +23,7 @@ import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; import static com.android.server.flags.Flags.newBugreportKeyboardShortcut; +import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut; import android.annotation.BinderThread; import android.annotation.MainThread; @@ -654,6 +655,18 @@ final class KeyGestureController { } } break; + case KeyEvent.KEYCODE_D: + if (enableMoveToNextDisplayShortcut()) { + if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) { + return handleKeyGesture(deviceId, new int[]{keyCode}, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, + KeyGestureEvent.ACTION_GESTURE_COMPLETE, + displayId, + focusedToken, /* flags = */0); + } + } + break; case KeyEvent.KEYCODE_SLASH: if (firstDown && event.isMetaPressed()) { return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java index 297cd68d5d3d..e16031cb664a 100644 --- a/services/core/java/com/android/server/input/PointerIconCache.java +++ b/services/core/java/com/android/server/input/PointerIconCache.java @@ -27,6 +27,7 @@ import android.hardware.display.DisplayManager; import android.os.Handler; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseDoubleArray; import android.util.SparseIntArray; import android.view.ContextThemeWrapper; import android.view.Display; @@ -34,6 +35,7 @@ import android.view.DisplayInfo; import android.view.PointerIcon; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.UiThread; import java.util.Objects; @@ -51,7 +53,7 @@ final class PointerIconCache { private final NativeInputManagerService mNative; // We use the UI thread for loading pointer icons. - private final Handler mUiThreadHandler = UiThread.getHandler(); + private final Handler mUiThreadHandler; @GuardedBy("mLoadedPointerIconsByDisplayAndType") private final SparseArray<SparseArray<PointerIcon>> mLoadedPointerIconsByDisplayAndType = @@ -70,6 +72,9 @@ final class PointerIconCache { POINTER_ICON_VECTOR_STYLE_STROKE_WHITE; @GuardedBy("mLoadedPointerIconsByDisplayAndType") private float mPointerIconScale = DEFAULT_POINTER_SCALE; + // Note that android doesn't have SparseFloatArray, so this falls back to use double instead. + @GuardedBy("mLoadedPointerIconsByDisplayAndType") + private final SparseDoubleArray mAccessibilityScaleFactorPerDisplay = new SparseDoubleArray(); private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @@ -86,6 +91,7 @@ final class PointerIconCache { mLoadedPointerIconsByDisplayAndType.remove(displayId); mDisplayContexts.remove(displayId); mDisplayDensities.delete(displayId); + mAccessibilityScaleFactorPerDisplay.delete(displayId); } } @@ -96,8 +102,15 @@ final class PointerIconCache { }; /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService) { + this(context, nativeService, UiThread.getHandler()); + } + + @VisibleForTesting + /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService, + Handler handler) { mContext = context; mNative = nativeService; + mUiThreadHandler = handler; } public void systemRunning() { @@ -134,6 +147,11 @@ final class PointerIconCache { mUiThreadHandler.post(() -> handleSetPointerScale(scale)); } + /** Set the scale for accessibility (magnification) for vector pointer icons. */ + public void setAccessibilityScaleFactor(int displayId, float scaleFactor) { + mUiThreadHandler.post(() -> handleAccessibilityScaleFactor(displayId, scaleFactor)); + } + /** * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if * it isn't already cached. @@ -155,8 +173,10 @@ final class PointerIconCache { /* force= */ true); theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(mPointerIconStrokeStyle), /* force= */ true); + final float scale = mPointerIconScale + * (float) mAccessibilityScaleFactorPerDisplay.get(displayId, 1f); icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme), - type, mUseLargePointerIcons, mPointerIconScale); + type, mUseLargePointerIcons, scale); iconsByType.put(type, icon); } return Objects.requireNonNull(icon); @@ -261,6 +281,19 @@ final class PointerIconCache { mNative.reloadPointerIcons(); } + @android.annotation.UiThread + private void handleAccessibilityScaleFactor(int displayId, float scale) { + synchronized (mLoadedPointerIconsByDisplayAndType) { + if (mAccessibilityScaleFactorPerDisplay.get(displayId, 1f) == scale) { + return; + } + mAccessibilityScaleFactorPerDisplay.put(displayId, scale); + // Clear cached icons on the display. + mLoadedPointerIconsByDisplayAndType.remove(displayId); + } + mNative.reloadPointerIcons(); + } + // Updates the cached display density for the given displayId, and returns true if // the cached density changed. @GuardedBy("mLoadedPointerIconsByDisplayAndType") diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index d1576c5cca4f..509fa3e1c9ba 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -127,42 +127,18 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { @BinderThread public void updateRuleSet( String version, ParceledListSlice<Rule> rules, IntentSender statusReceiver) { - String ruleProvider = getCallerPackageNameOrThrow(Binder.getCallingUid()); - if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format("Calling rule provider name is: %s.", ruleProvider)); + Intent intent = new Intent(); + intent.putExtra(EXTRA_STATUS, STATUS_SUCCESS); + try { + statusReceiver.sendIntent( + mContext, + /* code= */ 0, + intent, + /* onFinished= */ null, + /* handler= */ null); + } catch (Exception e) { + Slog.e(TAG, "Error sending status feedback.", e); } - - mHandler.post( - () -> { - boolean success = true; - try { - mIntegrityFileManager.writeRules(version, ruleProvider, rules.getList()); - } catch (Exception e) { - Slog.e(TAG, "Error writing rules.", e); - success = false; - } - - if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i( - TAG, - String.format( - "Successfully pushed rule set to version '%s' from '%s'", - version, ruleProvider)); - } - - Intent intent = new Intent(); - intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE); - try { - statusReceiver.sendIntent( - mContext, - /* code= */ 0, - intent, - /* onFinished= */ null, - /* handler= */ null); - } catch (Exception e) { - Slog.e(TAG, "Error sending status feedback.", e); - } - }); } @Override @@ -209,21 +185,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); } - /** We will use the SHA256 digest of a package name if it is more than 32 bytes long. */ - private String getPackageNameNormalized(String packageName) { - if (packageName.length() <= 32) { - return packageName; - } - - try { - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - byte[] hashBytes = messageDigest.digest(packageName.getBytes(StandardCharsets.UTF_8)); - return getHexDigest(hashBytes); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("SHA-256 algorithm not found", e); - } - } - private String getCallerPackageNameOrThrow(int callingUid) { String callerPackageName = getCallingRulePusherPackageName(callingUid); if (callerPackageName == null) { diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java index ea4a6db7a1e8..25741bc326e8 100644 --- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java +++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java @@ -81,6 +81,7 @@ import com.android.server.lights.LogicalLight; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -152,6 +153,8 @@ public final class NotificationAttentionHelper { @interface MuteReason {} private final Context mContext; + //This is NMS.mNotificationLock. + private final Object mLock; private final PackageManager mPackageManager; private final TelephonyManager mTelephonyManager; private final UserManager mUm; @@ -165,6 +168,7 @@ public final class NotificationAttentionHelper { private VibratorHelper mVibratorHelper; // The last key in this list owns the hardware. + @GuardedBy("mLock") ArrayList<String> mLights = new ArrayList<>(); private LogicalLight mNotificationLight; private LogicalLight mAttentionLight; @@ -183,8 +187,10 @@ public final class NotificationAttentionHelper { private String mVibrateNotificationKey; private boolean mSystemReady; private boolean mInCallStateOffHook = false; + @GuardedBy("mLock") private boolean mScreenOn = true; private boolean mUserPresent = false; + @GuardedBy("mLock") private boolean mNotificationPulseEnabled; private final Uri mInCallNotificationUri; private final AudioAttributes mInCallNotificationAudioAttributes; @@ -200,12 +206,13 @@ public final class NotificationAttentionHelper { private final PolitenessStrategy mStrategy; private int mCurrentWorkProfileId = UserHandle.USER_NULL; - public NotificationAttentionHelper(Context context, LightsManager lightsManager, + public NotificationAttentionHelper(Context context, Object lock, LightsManager lightsManager, AccessibilityManager accessibilityManager, PackageManager packageManager, UserManager userManager, NotificationUsageStats usageStats, NotificationManagerPrivate notificationManagerPrivate, ZenModeHelper zenModeHelper, SystemUiSystemPropertiesFlags.FlagResolver flagResolver) { mContext = context; + mLock = lock; mPackageManager = packageManager; mTelephonyManager = context.getSystemService(TelephonyManager.class); mAccessibilityManager = accessibilityManager; @@ -368,9 +375,11 @@ public final class NotificationAttentionHelper { private void loadUserSettings() { boolean pulseEnabled = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0; - if (mNotificationPulseEnabled != pulseEnabled) { - mNotificationPulseEnabled = pulseEnabled; - updateLightsLocked(); + synchronized (mLock) { + if (mNotificationPulseEnabled != pulseEnabled) { + mNotificationPulseEnabled = pulseEnabled; + updateLightsLocked(); + } } if (Flags.politeNotifications()) { @@ -1148,7 +1157,8 @@ public final class NotificationAttentionHelper { } } - public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) { + public void dumpLocked(PrintWriter pw, String prefix, + NotificationManagerService.DumpFilter filter) { pw.println("\n Notification attention state:"); pw.print(prefix); pw.println(" mSoundNotificationKey=" + mSoundNotificationKey); @@ -1684,16 +1694,22 @@ public final class NotificationAttentionHelper { if (action.equals(Intent.ACTION_SCREEN_ON)) { // Keep track of screen on/off state, but do not turn off the notification light // until user passes through the lock screen or views the notification. - mScreenOn = true; - updateLightsLocked(); + synchronized (mLock) { + mScreenOn = true; + updateLightsLocked(); + } } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { - mScreenOn = false; - mUserPresent = false; - updateLightsLocked(); + synchronized (mLock) { + mScreenOn = false; + mUserPresent = false; + updateLightsLocked(); + } } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { mInCallStateOffHook = TelephonyManager.EXTRA_STATE_OFFHOOK .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); - updateLightsLocked(); + synchronized (mLock) { + updateLightsLocked(); + } } else if (action.equals(Intent.ACTION_USER_PRESENT)) { mUserPresent = true; // turn off LED when user passes through lock screen @@ -1755,9 +1771,11 @@ public final class NotificationAttentionHelper { Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0; - if (mNotificationPulseEnabled != pulseEnabled) { - mNotificationPulseEnabled = pulseEnabled; - updateLightsLocked(); + synchronized (mLock) { + if (mNotificationPulseEnabled != pulseEnabled) { + mNotificationPulseEnabled = pulseEnabled; + updateLightsLocked(); + } } } if (Flags.politeNotifications()) { @@ -1840,7 +1858,9 @@ public final class NotificationAttentionHelper { @VisibleForTesting void setScreenOn(boolean on) { - mScreenOn = on; + synchronized (mLock) { + mScreenOn = on; + } } @VisibleForTesting diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e48c8ee718e3..48cc03221124 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2640,9 +2640,9 @@ public class NotificationManagerService extends SystemService { mToastRateLimiter = toastRateLimiter; - mAttentionHelper = new NotificationAttentionHelper(getContext(), lightsManager, - mAccessibilityManager, mPackageManagerClient, userManager, usageStats, - mNotificationManagerPrivate, mZenModeHelper, flagResolver); + mAttentionHelper = new NotificationAttentionHelper(getContext(), mNotificationLock, + lightsManager, mAccessibilityManager, mPackageManagerClient, userManager, + usageStats, mNotificationManagerPrivate, mZenModeHelper, flagResolver); // register for various Intents. // If this is called within a test, make sure to unregister the intent receivers by @@ -7331,7 +7331,7 @@ public class NotificationManagerService extends SystemService { pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate); pw.println(" hideSilentStatusBar=" + mPreferencesHelper.shouldHideSilentStatusIcons()); - mAttentionHelper.dump(pw, " ", filter); + mAttentionHelper.dumpLocked(pw, " ", filter); } pw.println(" mArchive=" + mArchive.toString()); mArchive.dumpImpl(pw, filter); diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java index a221152222ee..d1d6ed0f1f99 100644 --- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java +++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java @@ -34,16 +34,19 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.audiopolicy.AudioPolicy; +import android.multiuser.Flags; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.util.ArraySet; import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.util.List; +import java.util.Set; public class BackgroundUserSoundNotifier { @@ -65,6 +68,10 @@ public class BackgroundUserSoundNotifier { */ @VisibleForTesting int mNotificationClientUid = -1; + /** + * UIDs of audio focus infos with active notifications. + */ + Set<Integer> mNotificationClientUids = new ArraySet<>(); @VisibleForTesting AudioPolicy mFocusControlAudioPolicy; @VisibleForTesting @@ -149,26 +156,42 @@ public class BackgroundUserSoundNotifier { @SuppressLint("MissingPermission") @Override public void onReceive(Context context, Intent intent) { - if (mNotificationClientUid == -1) { - return; + if (Flags.multipleAlarmNotificationsSupport()) { + if (!intent.hasExtra(EXTRA_NOTIFICATION_CLIENT_UID)) { + return; + } + } else { + if (mNotificationClientUid == -1) { + return; + } } - dismissNotification(mNotificationClientUid); + + int clientUid; + if (Flags.multipleAlarmNotificationsSupport()) { + clientUid = intent.getIntExtra(EXTRA_NOTIFICATION_CLIENT_UID, -1); + } else { + clientUid = mNotificationClientUid; + } + dismissNotification(clientUid); if (DEBUG) { final int actionIndex = intent.getAction().lastIndexOf(".") + 1; final String action = intent.getAction().substring(actionIndex); Log.d(LOG_TAG, "Action requested: " + action + ", by userId " + ActivityManager.getCurrentUser() + " for alarm on user " - + UserHandle.getUserHandleForUid(mNotificationClientUid)); + + UserHandle.getUserHandleForUid(clientUid)); } if (ACTION_MUTE_SOUND.equals(intent.getAction())) { - muteAlarmSounds(mNotificationClientUid); + muteAlarmSounds(clientUid); } else if (ACTION_SWITCH_USER.equals(intent.getAction())) { - activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid)); + activityManager.switchUser(UserHandle.getUserId(clientUid)); + } + if (Flags.multipleAlarmNotificationsSupport()) { + mNotificationClientUids.remove(clientUid); + } else { + mNotificationClientUid = -1; } - - mNotificationClientUid = -1; } }; @@ -215,10 +238,11 @@ public class BackgroundUserSoundNotifier { final int userId = UserHandle.getUserId(afi.getClientUid()); final int usage = afi.getAttributes().getUsage(); UserInfo userInfo = mUserManager.getUserInfo(userId); + // Only show notification if the sound is coming from background user and the notification - // is not already shown. + // for this UID is not already shown. if (userInfo != null && userId != foregroundContext.getUserId() - && mNotificationClientUid == -1) { + && !isNotificationShown(afi.getClientUid())) { //TODO: b/349138482 - Add handling of cases when usage == USAGE_NOTIFICATION_RINGTONE if (usage == USAGE_ALARM) { if (DEBUG) { @@ -226,8 +250,11 @@ public class BackgroundUserSoundNotifier { + ", displaying notification for current user " + foregroundContext.getUserId()); } - - mNotificationClientUid = afi.getClientUid(); + if (Flags.multipleAlarmNotificationsSupport()) { + mNotificationClientUids.add(afi.getClientUid()); + } else { + mNotificationClientUid = afi.getClientUid(); + } mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(), createNotification(userInfo.name, foregroundContext, afi.getClientUid()), @@ -243,15 +270,21 @@ public class BackgroundUserSoundNotifier { */ @VisibleForTesting void dismissNotificationIfNecessary(int notificationClientUid) { + if (getAudioFocusInfoForNotification(notificationClientUid) == null - && mNotificationClientUid >= 0) { + && isNotificationShown(notificationClientUid)) { if (DEBUG) { Log.d(LOG_TAG, "Alarm ringing on background user " + UserHandle.getUserHandleForUid(notificationClientUid).getIdentifier() + " left focus stack, dismissing notification"); } dismissNotification(notificationClientUid); - mNotificationClientUid = -1; + + if (Flags.multipleAlarmNotificationsSupport()) { + mNotificationClientUids.remove(notificationClientUid); + } else { + mNotificationClientUid = -1; + } } } @@ -331,4 +364,12 @@ public class BackgroundUserSoundNotifier { return notificationBuilder.build(); } + + private boolean isNotificationShown(int notificationClientUid) { + if (Flags.multipleAlarmNotificationsSupport()) { + return mNotificationClientUids.contains(notificationClientUid); + } else { + return mNotificationClientUid != -1; + } + } } diff --git a/services/core/java/com/android/server/power/PowerManagerShellCommand.java b/services/core/java/com/android/server/power/PowerManagerShellCommand.java index f69a017fc45a..35a69a29d16a 100644 --- a/services/core/java/com/android/server/power/PowerManagerShellCommand.java +++ b/services/core/java/com/android/server/power/PowerManagerShellCommand.java @@ -22,6 +22,8 @@ import android.app.IAlarmListener; import android.app.IAlarmManager; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManagerInternal; +import android.os.Binder; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.PowerManagerInternal; @@ -32,6 +34,9 @@ import android.os.SystemClock; import android.util.SparseArray; import android.view.Display; +import com.android.server.LocalServices; +import com.android.server.pm.pkg.AndroidPackage; + import java.io.PrintWriter; import java.util.List; @@ -266,11 +271,18 @@ class PowerManagerShellCommand extends ShellCommand { ServiceManager.getService(Context.ALARM_SERVICE)); } try { - // This command is called by the shell, which has "com.android.shell" as package - // name. - pw.println("Schedule an alarm to wakeup in " - + delayMillis + " ms, on behalf of shell."); - mAlarmManager.set("com.android.shell", + PackageManagerInternal packageManagerInternal = + LocalServices.getService(PackageManagerInternal.class); + AndroidPackage callingPackage = + packageManagerInternal.getPackage(Binder.getCallingUid()); + if (callingPackage == null) { + pw.println("Calling uid " + Binder.getCallingUid() + " is not an android" + + " package. Cannot schedule a delayed wakeup on behalf of it."); + return -1; + } + pw.println("Schedule an alarm to wakeup in " + delayMillis + + " ms, on behalf of " + callingPackage.getPackageName()); + mAlarmManager.set(callingPackage.getPackageName(), AlarmManager.RTC_WAKEUP, wakeUpTime, 0, 0, AlarmManager.FLAG_PRIORITIZE, null, mAlarmListener, "PowerManagerShellCommand", null, null); diff --git a/services/core/java/com/android/server/power/stats/AccumulatedBatteryUsageStatsSection.java b/services/core/java/com/android/server/power/stats/AccumulatedBatteryUsageStatsSection.java index dd6d5dbf753c..c0a06fcfdeaa 100644 --- a/services/core/java/com/android/server/power/stats/AccumulatedBatteryUsageStatsSection.java +++ b/services/core/java/com/android/server/power/stats/AccumulatedBatteryUsageStatsSection.java @@ -51,6 +51,11 @@ class AccumulatedBatteryUsageStatsSection extends PowerStatsSpan.Section { mBatteryUsageStats.build().dump(ipw, ""); } + @Override + public void close() { + mBatteryUsageStats.discard(); + } + static class Reader implements PowerStatsSpan.SectionReader { @Override public String getType() { diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index c04158fe5243..48174a6bad11 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -11509,9 +11509,6 @@ public class BatteryStatsImpl extends BatteryStats { mOnBatteryTimeBase); } - mPerDisplayBatteryStats = new DisplayBatteryStats[1]; - mPerDisplayBatteryStats[0] = new DisplayBatteryStats(mClock, mOnBatteryTimeBase); - mInteractiveTimer = new StopwatchTimer(mClock, null, -10, null, mOnBatteryTimeBase); mPowerSaveModeEnabledTimer = new StopwatchTimer(mClock, null, -2, null, mOnBatteryTimeBase); diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index 265f1dfce90f..b996c43d3dd5 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -295,7 +295,8 @@ public class BatteryUsageStatsProvider { stats.builder = ((AccumulatedBatteryUsageStatsSection) section) .getBatteryUsageStatsBuilder(); stats.startWallClockTime = powerStatsSpan.getMetadata().getStartTime(); - stats.startMonotonicTime = powerStatsSpan.getMetadata().getStartMonotonicTime(); + stats.startMonotonicTime = + powerStatsSpan.getMetadata().getStartMonotonicTime(); stats.endMonotonicTime = powerStatsSpan.getMetadata().getEndMonotonicTime(); break; } @@ -484,29 +485,30 @@ public class BatteryUsageStatsProvider { continue; } - PowerStatsSpan powerStatsSpan = mPowerStatsStore.loadPowerStatsSpan( - spanMetadata.getId(), BatteryUsageStatsSection.TYPE); - if (powerStatsSpan == null) { - continue; - } - - for (PowerStatsSpan.Section section : powerStatsSpan.getSections()) { - BatteryUsageStats snapshot = - ((BatteryUsageStatsSection) section).getBatteryUsageStats(); - if (!Arrays.equals(snapshot.getCustomPowerComponentNames(), - customEnergyConsumerNames)) { - Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different " - + "custom power components: " - + Arrays.toString(snapshot.getCustomPowerComponentNames())); + try (PowerStatsSpan powerStatsSpan = mPowerStatsStore.loadPowerStatsSpan( + spanMetadata.getId(), BatteryUsageStatsSection.TYPE)) { + if (powerStatsSpan == null) { continue; } - if (includeProcessStateData && !snapshot.isProcessStateDataIncluded()) { - Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which " - + " does not include process state data"); - continue; + for (PowerStatsSpan.Section section : powerStatsSpan.getSections()) { + BatteryUsageStats snapshot = + ((BatteryUsageStatsSection) section).getBatteryUsageStats(); + if (!Arrays.equals(snapshot.getCustomPowerComponentNames(), + customEnergyConsumerNames)) { + Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different " + + "custom power components: " + + Arrays.toString(snapshot.getCustomPowerComponentNames())); + continue; + } + + if (includeProcessStateData && !snapshot.isProcessStateDataIncluded()) { + Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which " + + " does not include process state data"); + continue; + } + builder.add(snapshot); } - builder.add(snapshot); } } return builder.build(); diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java index af3652475376..eb896e9d4eee 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java @@ -18,6 +18,7 @@ package com.android.server.power.stats; import android.os.BatteryUsageStats; import android.util.IndentingPrintWriter; +import android.util.Slog; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -28,6 +29,7 @@ import java.io.IOException; class BatteryUsageStatsSection extends PowerStatsSpan.Section { public static final String TYPE = "battery-usage-stats"; + private static final String TAG = "BatteryUsageStatsSection"; private final BatteryUsageStats mBatteryUsageStats; @@ -50,6 +52,15 @@ class BatteryUsageStatsSection extends PowerStatsSpan.Section { mBatteryUsageStats.dump(ipw, ""); } + @Override + public void close() { + try { + mBatteryUsageStats.close(); + } catch (IOException e) { + Slog.e(TAG, "Closing BatteryUsageStats", e); + } + } + static class Reader implements PowerStatsSpan.SectionReader { @Override public String getType() { diff --git a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java index 5105272e6980..4f560cf68f8e 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java @@ -51,7 +51,7 @@ import java.util.Set; /** * Contains power stats of various kinds, aggregated over a time span. */ -public class PowerStatsSpan { +public class PowerStatsSpan implements AutoCloseable { private static final String TAG = "PowerStatsStore"; /** @@ -321,6 +321,13 @@ public class PowerStatsSpan { public void dump(IndentingPrintWriter ipw) { ipw.println(mType); } + + /** + * Closes the section, releasing any resources it held. Once closed, the Section + * should not be used. + */ + public void close() { + } } /** @@ -484,4 +491,10 @@ public class PowerStatsSpan { ipw.decreaseIndent(); } } + @Override + public void close() { + for (int i = 0; i < mSections.size(); i++) { + mSections.get(i).close(); + } + } } diff --git a/services/core/java/com/android/server/power/stats/PowerStatsStore.java b/services/core/java/com/android/server/power/stats/PowerStatsStore.java index 3673617f6104..d83d355fce31 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsStore.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsStore.java @@ -331,9 +331,10 @@ public class PowerStatsStore { ipw.increaseIndent(); List<PowerStatsSpan.Metadata> contents = getTableOfContents(); for (PowerStatsSpan.Metadata metadata : contents) { - PowerStatsSpan span = loadPowerStatsSpan(metadata.getId()); - if (span != null) { - span.dump(ipw); + try (PowerStatsSpan span = loadPowerStatsSpan(metadata.getId())) { + if (span != null) { + span.dump(ipw); + } } } ipw.decreaseIndent(); diff --git a/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java index 32dfdf915bca..5f93bdf07f47 100644 --- a/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java @@ -50,12 +50,14 @@ class AmbientDisplayPowerStatsProcessor extends PowerStatsProcessor { return; } - if (mScreenPowerStatsDescriptor == null) { - mScreenPowerStatsDescriptor = screenStats.getPowerStatsDescriptor(); - if (mScreenPowerStatsDescriptor == null) { - return; - } + PowerStats.Descriptor screenDescriptor = screenStats.getPowerStatsDescriptor(); + if (screenDescriptor == null) { + return; + } + if (mScreenPowerStatsDescriptor == null + || !mScreenPowerStatsDescriptor.equals(screenDescriptor)) { + mScreenPowerStatsDescriptor = screenDescriptor; mScreenPowerStatsLayout = new ScreenPowerStatsLayout(mScreenPowerStatsDescriptor); mTmpScreenStats = new long[mScreenPowerStatsDescriptor.statsArrayLength]; } diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java index c8170a1982bb..b2442c81b2e5 100644 --- a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java +++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java @@ -105,19 +105,19 @@ class PowerStatsExporter { maxEndTime = spanMaxTime; } - PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(), - AggregatedPowerStatsSection.TYPE); - if (span == null) { - Slog.e(TAG, "Could not read PowerStatsStore section " + metadata); - continue; - } - List<PowerStatsSpan.Section> sections = span.getSections(); - for (int k = 0; k < sections.size(); k++) { - hasStoredSpans = true; - PowerStatsSpan.Section section = sections.get(k); - populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, - ((AggregatedPowerStatsSection) section).getAggregatedPowerStats()); - // TODO(b/371614748): close the builder + try (PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(), + AggregatedPowerStatsSection.TYPE)) { + if (span == null) { + Slog.e(TAG, "Could not read PowerStatsStore section " + metadata); + continue; + } + List<PowerStatsSpan.Section> sections = span.getSections(); + for (int k = 0; k < sections.size(); k++) { + hasStoredSpans = true; + PowerStatsSpan.Section section = sections.get(k); + populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, + ((AggregatedPowerStatsSection) section).getAggregatedPowerStats()); + } } } diff --git a/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java index b295e309d4fb..8e7498f38fcb 100644 --- a/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java @@ -89,14 +89,15 @@ class ScreenPowerStatsProcessor extends PowerStatsProcessor { return true; } - mLastUsedDescriptor = descriptor; - mStatsLayout = new ScreenPowerStatsLayout(descriptor); - if (mStatsLayout.getDisplayCount() != mDisplayCount) { - Slog.e(TAG, "Incompatible number of displays: " + mStatsLayout.getDisplayCount() + ScreenPowerStatsLayout statsLayout = new ScreenPowerStatsLayout(descriptor); + if (statsLayout.getDisplayCount() != mDisplayCount) { + Slog.e(TAG, "Incompatible number of displays: " + statsLayout.getDisplayCount() + ", expected: " + mDisplayCount); return false; } + mLastUsedDescriptor = descriptor; + mStatsLayout = statsLayout; mTmpDeviceStatsArray = new long[descriptor.statsArrayLength]; mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength]; return true; diff --git a/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java b/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java new file mode 100644 index 000000000000..caca011b6549 --- /dev/null +++ b/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java @@ -0,0 +1,153 @@ +/* + * 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.security.forensic; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.security.forensic.ForensicEvent; +import android.security.forensic.IBackupTransport; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.infra.AndroidFuture; + +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class BackupTransportConnection implements ServiceConnection { + private static final String TAG = "BackupTransportConnection"; + private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 mins + private final Context mContext; + private String mForensicBackupTransportConfig; + volatile IBackupTransport mService; + + public BackupTransportConnection(Context context) { + mContext = context; + mService = null; + } + + /** + * Initialize the BackupTransport binder service. + * @return Whether the initialization succeed. + */ + public boolean initialize() { + if (!bindService()) { + return false; + } + AndroidFuture<Integer> resultFuture = new AndroidFuture<>(); + try { + mService.initialize(resultFuture); + } catch (RemoteException e) { + Slog.e(TAG, "Remote Exception", e); + unbindService(); + return false; + } + Integer result = getFutureResult(resultFuture); + if (result != null && result == 0) { + return true; + } else { + unbindService(); + return false; + } + } + + /** + * Add data to the BackupTransport binder service. + * @param data List of ForensicEvent. + * @return Whether the data is added to the binder service. + */ + public boolean addData(List<ForensicEvent> data) { + AndroidFuture<Integer> resultFuture = new AndroidFuture<>(); + try { + mService.addData(data, resultFuture); + } catch (RemoteException e) { + Slog.e(TAG, "Remote Exception", e); + return false; + } + Integer result = getFutureResult(resultFuture); + return result != null && result == 0; + } + + /** + * Release the BackupTransport binder service. + */ + public void release() { + AndroidFuture<Integer> resultFuture = new AndroidFuture<>(); + try { + mService.release(resultFuture); + } catch (RemoteException e) { + Slog.e(TAG, "Remote Exception", e); + } finally { + unbindService(); + } + } + + private <T> T getFutureResult(AndroidFuture<T> future) { + try { + return future.get(FUTURE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException + | CancellationException e) { + Slog.w(TAG, "Failed to get result from transport:", e); + return null; + } + } + + private boolean bindService() { + mForensicBackupTransportConfig = mContext.getString( + com.android.internal.R.string.config_forensicBackupTransport); + if (TextUtils.isEmpty(mForensicBackupTransportConfig)) { + return false; + } + + ComponentName serviceComponent = + ComponentName.unflattenFromString(mForensicBackupTransportConfig); + if (serviceComponent == null) { + return false; + } + + Intent intent = new Intent().setComponent(serviceComponent); + boolean result = mContext.bindServiceAsUser( + intent, this, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); + if (!result) { + unbindService(); + } + return result; + } + + private void unbindService() { + mContext.unbindService(this); + mService = null; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mService = IBackupTransport.Stub.asInterface(service); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + } +} diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java index 07639d1a3945..20c648eb61c2 100644 --- a/services/core/java/com/android/server/security/forensic/ForensicService.java +++ b/services/core/java/com/android/server/security/forensic/ForensicService.java @@ -63,6 +63,7 @@ public class ForensicService extends SystemService { private final Context mContext; private final Handler mHandler; + private final BackupTransportConnection mBackupTransportConnection; private final BinderService mBinderService; private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>(); @@ -77,6 +78,7 @@ public class ForensicService extends SystemService { super(injector.getContext()); mContext = injector.getContext(); mHandler = new EventHandler(injector.getLooper(), this); + mBackupTransportConnection = injector.getBackupTransportConnection(); mBinderService = new BinderService(this); } @@ -221,6 +223,10 @@ public class ForensicService extends SystemService { private void enable(IForensicServiceCommandCallback callback) throws RemoteException { switch (mState) { case STATE_VISIBLE: + if (!mBackupTransportConnection.initialize()) { + callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE); + break; + } mState = STATE_ENABLED; notifyStateMonitors(); callback.onSuccess(); @@ -236,6 +242,7 @@ public class ForensicService extends SystemService { private void disable(IForensicServiceCommandCallback callback) throws RemoteException { switch (mState) { case STATE_ENABLED: + mBackupTransportConnection.release(); mState = STATE_VISIBLE; notifyStateMonitors(); callback.onSuccess(); @@ -266,6 +273,8 @@ public class ForensicService extends SystemService { Context getContext(); Looper getLooper(); + + BackupTransportConnection getBackupTransportConnection(); } private static final class InjectorImpl implements Injector { @@ -289,6 +298,11 @@ public class ForensicService extends SystemService { serviceThread.start(); return serviceThread.getLooper(); } + + @Override + public BackupTransportConnection getBackupTransportConnection() { + return new BackupTransportConnection(mContext); + } } } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index e7735d8480f8..54e4f8e9a110 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -48,6 +48,9 @@ import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; import static android.util.MathUtils.constrain; import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID; +import static com.android.internal.os.ProcfsMemoryUtil.getProcessCmdlines; +import static com.android.internal.os.ProcfsMemoryUtil.readCmdlineFromProcfs; +import static com.android.internal.os.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY; @@ -68,9 +71,6 @@ import static com.android.server.stats.Flags.addMobileBytesTransferByProcStatePu import static com.android.server.stats.Flags.applyNetworkStatsPollRateLimit; import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; -import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines; -import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs; -import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; import static com.android.server.stats.pull.netstats.NetworkStatsUtils.fromPublicNetworkStats; import static com.android.server.stats.pull.netstats.NetworkStatsUtils.isAddEntriesSupported; @@ -209,6 +209,8 @@ import com.android.internal.os.KernelSingleProcessCpuThreadReader.ProcessCpuUsag import com.android.internal.os.LooperStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.ProcessCpuTracker; +import com.android.internal.os.ProcfsMemoryUtil; +import com.android.internal.os.ProcfsMemoryUtil.MemorySnapshot; import com.android.internal.os.SelectedProcessCpuThreadReader; import com.android.internal.os.StoragedUidIoStatsReader; import com.android.internal.util.CollectionUtils; @@ -229,7 +231,6 @@ import com.android.server.power.stats.KernelWakelockReader; import com.android.server.power.stats.KernelWakelockStats; import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import com.android.server.stats.pull.IonMemoryUtil.IonAllocations; -import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot; import com.android.server.stats.pull.netstats.NetworkStatsAccumulator; import com.android.server.stats.pull.netstats.NetworkStatsExt; import com.android.server.stats.pull.netstats.SubInfo; diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java index 1e82b8999834..baf84cf4aa8b 100644 --- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -40,11 +40,11 @@ import android.telephony.TelephonyManager; import android.telephony.TelephonyManager.CarrierPrivilegesCallback; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 5bc2c2dface9..1fba29779f0f 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -47,11 +47,11 @@ import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.VcnManagementService.VcnCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.util.LogUtils; diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index a81ad22ddf4a..189b6089186e 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -90,11 +90,11 @@ import android.os.SystemClock; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java index 31ee2477fa64..78ff432f5423 100644 --- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java +++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java @@ -36,11 +36,11 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.internal.util.IndentingPrintWriter; import java.util.Objects; import java.util.Set; diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index ad5bc7294f85..0b9b677df16a 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -46,11 +46,11 @@ import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback; diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java index 53b0751d196e..448a7ebfffd8 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java @@ -29,11 +29,11 @@ import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.os.Handler; import android.os.ParcelUuid; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index 7ab8e552722a..1945052b92df 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -22,10 +22,10 @@ import android.annotation.Nullable; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; +import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.internal.util.IndentingPrintWriter; import java.util.Objects; diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index c1f5a27b81e7..a2c2dfc0a5be 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -478,10 +478,10 @@ public class ActivityStartController { intentGrants.merge(creatorIntentGrants); } } catch (SecurityException securityException) { - ActivityStarter.logForIntentRedirect( + ActivityStarter.logAndThrowExceptionForIntentRedirect( "Creator URI Grant Caused Exception.", intent, creatorUid, - creatorPackage, filterCallingUid, callingPackage); - // TODO b/368559093 - rethrow the securityException. + creatorPackage, filterCallingUid, callingPackage, + securityException); } } if ((aInfo.applicationInfo.privateFlags diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 5d3ae54f0934..2c4179fb6d88 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -54,6 +54,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static android.content.pm.ActivityInfo.launchModeToString; import static android.os.Process.INVALID_UID; +import static android.security.Flags.preventIntentRedirectAbortOrThrowException; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; @@ -100,9 +101,11 @@ import android.app.PendingIntent; import android.app.ProfilerInfo; import android.app.WaitResult; import android.app.WindowConfiguration; +import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; import android.compat.annotation.EnabledSince; +import android.compat.annotation.Overridable; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; @@ -188,6 +191,10 @@ class ActivityStarter { @Disabled static final long ASM_RESTRICTIONS = 230590090L; + @ChangeId + @Overridable + private static final long ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION = 29623414L; + private final ActivityTaskManagerService mService; private final RootWindowContainer mRootWindowContainer; private final ActivityTaskSupervisor mSupervisor; @@ -608,11 +615,10 @@ class ActivityStarter { // Check if the Intent was redirected if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN) != 0) { - ActivityStarter.logForIntentRedirect( + ActivityStarter.logAndThrowExceptionForIntentRedirect( "Unparceled intent does not have a creator token set.", intent, - intentCreatorUid, - intentCreatorPackage, resolvedCallingUid, resolvedCallingPackage); - // TODO b/368559093 - eventually ramp up to throw SecurityException + intentCreatorUid, intentCreatorPackage, resolvedCallingUid, + resolvedCallingPackage, null); } if (IntentCreatorToken.isValid(intent)) { IntentCreatorToken creatorToken = (IntentCreatorToken) intent.getCreatorToken(); @@ -645,11 +651,10 @@ class ActivityStarter { intentGrants.merge(creatorIntentGrants); } } catch (SecurityException securityException) { - ActivityStarter.logForIntentRedirect( + ActivityStarter.logAndThrowExceptionForIntentRedirect( "Creator URI Grant Caused Exception.", intent, intentCreatorUid, intentCreatorPackage, resolvedCallingUid, - resolvedCallingPackage); - // TODO b/368559093 - rethrow the securityException. + resolvedCallingPackage, securityException); } } } else { @@ -670,11 +675,10 @@ class ActivityStarter { intentGrants.merge(creatorIntentGrants); } } catch (SecurityException securityException) { - ActivityStarter.logForIntentRedirect( + ActivityStarter.logAndThrowExceptionForIntentRedirect( "Creator URI Grant Caused Exception.", intent, intentCreatorUid, intentCreatorPackage, resolvedCallingUid, - resolvedCallingPackage); - // TODO b/368559093 - rethrow the securityException. + resolvedCallingPackage, securityException); } } } @@ -1045,7 +1049,7 @@ class ActivityStarter { int callingUid = request.callingUid; int intentCreatorUid = request.intentCreatorUid; String intentCreatorPackage = request.intentCreatorPackage; - String intentCallingPackage = request.callingPackage; + String callingPackage = request.callingPackage; String callingFeatureId = request.callingFeatureId; final int realCallingPid = request.realCallingPid; final int realCallingUid = request.realCallingUid; @@ -1130,7 +1134,7 @@ class ActivityStarter { // launched in the app flow to redirect to an activity picked by the user, where // we want the final activity to consider it to have been launched by the // previous app activity. - intentCallingPackage = sourceRecord.launchedFromPackage; + callingPackage = sourceRecord.launchedFromPackage; callingFeatureId = sourceRecord.launchedFromFeatureId; } } @@ -1152,7 +1156,7 @@ class ActivityStarter { if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) { err = packageArchiver .requestUnarchiveOnActivityStart( - intent, intentCallingPackage, mRequest.userId, realCallingUid); + intent, callingPackage, mRequest.userId, realCallingUid); } } } @@ -1211,7 +1215,7 @@ class ActivityStarter { boolean abort; try { abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, - requestCode, callingPid, callingUid, intentCallingPackage, callingFeatureId, + requestCode, callingPid, callingUid, callingPackage, callingFeatureId, request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultRootTask); } catch (SecurityException e) { @@ -1239,7 +1243,7 @@ class ActivityStarter { abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, - intentCallingPackage); + callingPackage); if (intentCreatorUid != Request.DEFAULT_INTENT_CREATOR_UID) { try { @@ -1247,36 +1251,29 @@ class ActivityStarter { requestCode, 0, intentCreatorUid, intentCreatorPackage, "", request.ignoreTargetSecurity, inTask != null, null, resultRecord, resultRootTask)) { - logForIntentRedirect("Creator checkStartAnyActivityPermission Caused abortion.", + abort = logAndAbortForIntentRedirect( + "Creator checkStartAnyActivityPermission Caused abortion.", intent, intentCreatorUid, intentCreatorPackage, callingUid, - intentCallingPackage); - // TODO b/368559093 - set abort to true. - // abort = true; + callingPackage); } } catch (SecurityException e) { - logForIntentRedirect("Creator checkStartAnyActivityPermission Caused Exception.", - intent, intentCreatorUid, intentCreatorPackage, callingUid, - intentCallingPackage); - // TODO b/368559093 - rethrow the exception. - //throw e; - } - if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid, 0, - resolvedType, aInfo.applicationInfo)) { - logForIntentRedirect("Creator IntentFirewall.checkStartActivity Caused abortion.", - intent, intentCreatorUid, intentCreatorPackage, callingUid, - intentCallingPackage); - // TODO b/368559093 - set abort to true. - // abort = true; - } - - if (!mService.getPermissionPolicyInternal().checkStartActivity(intent, intentCreatorUid, - intentCreatorPackage)) { - logForIntentRedirect( + logAndThrowExceptionForIntentRedirect( + "Creator checkStartAnyActivityPermission Caused Exception.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage, + e); + } + if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid, + 0, resolvedType, aInfo.applicationInfo)) { + abort = logAndAbortForIntentRedirect( + "Creator IntentFirewall.checkStartActivity Caused abortion.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage); + } + + if (!mService.getPermissionPolicyInternal().checkStartActivity(intent, + intentCreatorUid, intentCreatorPackage)) { + abort = logAndAbortForIntentRedirect( "Creator PermissionPolicyService.checkStartActivity Caused abortion.", - intent, intentCreatorUid, intentCreatorPackage, callingUid, - intentCallingPackage); - // TODO b/368559093 - set abort to true. - // abort = true; + intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage); } intent.removeCreatorTokenInfo(); } @@ -1296,7 +1293,7 @@ class ActivityStarter { balController.checkBackgroundActivityStart( callingUid, callingPid, - intentCallingPackage, + callingPackage, realCallingUid, realCallingPid, callerApp, @@ -1317,7 +1314,7 @@ class ActivityStarter { if (request.allowPendingRemoteAnimationRegistryLookup) { checkedOptions = mService.getActivityStartController() .getPendingRemoteAnimationRegistry() - .overrideOptionsIfNeeded(intentCallingPackage, checkedOptions); + .overrideOptionsIfNeeded(callingPackage, checkedOptions); } if (mService.mController != null) { try { @@ -1334,7 +1331,7 @@ class ActivityStarter { final TaskDisplayArea suggestedLaunchDisplayArea = computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions); mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, - intentCallingPackage, + callingPackage, callingFeatureId); if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) { @@ -1372,7 +1369,7 @@ class ActivityStarter { if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( aInfo.packageName, userId)) { final IIntentSender target = mService.getIntentSenderLocked( - ActivityManager.INTENT_SENDER_ACTIVITY, intentCallingPackage, + ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId, callingUid, userId, null, null, 0, new Intent[]{intent}, new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT @@ -1436,7 +1433,7 @@ class ActivityStarter { // app [on install success]. if (rInfo != null && rInfo.auxiliaryInfo != null) { intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent, - intentCallingPackage, callingFeatureId, verificationBundle, resolvedType, + callingPackage, callingFeatureId, verificationBundle, resolvedType, userId); resolvedType = null; callingUid = realCallingUid; @@ -1460,7 +1457,7 @@ class ActivityStarter { .setCaller(callerApp) .setLaunchedFromPid(callingPid) .setLaunchedFromUid(callingUid) - .setLaunchedFromPackage(intentCallingPackage) + .setLaunchedFromPackage(callingPackage) .setLaunchedFromFeature(callingFeatureId) .setIntent(intent) .setResolvedType(resolvedType) @@ -3588,16 +3585,32 @@ class ActivityStarter { pw.println(mInTaskFragment); } - static void logForIntentRedirect(String message, Intent intent, int intentCreatorUid, - String intentCreatorPackage, int callingUid, String callingPackage) { + static void logAndThrowExceptionForIntentRedirect(@NonNull String message, + @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage, + int callingUid, @Nullable String callingPackage, + @Nullable SecurityException originalException) { + String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid, + intentCreatorPackage, callingUid, callingPackage); + Slog.wtf(TAG, msg); + if (preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled( + ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid)) { + throw new SecurityException(msg, originalException); + } + } + + private static boolean logAndAbortForIntentRedirect(@NonNull String message, + @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage, + int callingUid, @Nullable String callingPackage) { String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage); Slog.wtf(TAG, msg); + return preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled( + ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid); } - private static String getIntentRedirectPreventedLogMessage(String message, Intent intent, - int intentCreatorUid, String intentCreatorPackage, int callingUid, - String callingPackage) { + private static String getIntentRedirectPreventedLogMessage(@NonNull String message, + @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage, + int callingUid, @Nullable String callingPackage) { return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid + "; callingPackage: " + callingPackage + "; intent: " + intent; diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 4f71719006f5..567eca2f639a 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2572,14 +2572,21 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { wpc.computeProcessActivityState(); } - void computeProcessActivityStateBatch() { + boolean computeProcessActivityStateBatch() { if (mActivityStateChangedProcs.isEmpty()) { - return; + return false; } + boolean changed = false; for (int i = mActivityStateChangedProcs.size() - 1; i >= 0; i--) { - mActivityStateChangedProcs.get(i).computeProcessActivityState(); + final WindowProcessController wpc = mActivityStateChangedProcs.get(i); + final int prevState = wpc.getActivityStateFlags(); + wpc.computeProcessActivityState(); + if (!changed && prevState != wpc.getActivityStateFlags()) { + changed = true; + } } mActivityStateChangedProcs.clear(); + return changed; } /** diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java index d59046f44129..f1dd41e7d74a 100644 --- a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java @@ -39,6 +39,7 @@ import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION import static com.android.server.wm.AppCompatUtils.isChangeEnabled; import android.annotation.NonNull; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -126,18 +127,18 @@ class AppCompatAspectRatioOverrides { return false; } - mUserAspectRatioState.mUserAspectRatio = getUserMinAspectRatioOverrideCode(); + final int aspectRatio = getUserMinAspectRatioOverrideCode(); - return mUserAspectRatioState.mUserAspectRatio != USER_MIN_ASPECT_RATIO_UNSET - && mUserAspectRatioState.mUserAspectRatio != USER_MIN_ASPECT_RATIO_APP_DEFAULT - && mUserAspectRatioState.mUserAspectRatio != USER_MIN_ASPECT_RATIO_FULLSCREEN; + return aspectRatio != USER_MIN_ASPECT_RATIO_UNSET + && aspectRatio != USER_MIN_ASPECT_RATIO_APP_DEFAULT + && aspectRatio != USER_MIN_ASPECT_RATIO_FULLSCREEN; } boolean shouldApplyUserFullscreenOverride() { if (isUserFullscreenOverrideEnabled()) { - mUserAspectRatioState.mUserAspectRatio = getUserMinAspectRatioOverrideCode(); + final int aspectRatio = getUserMinAspectRatioOverrideCode(); - return mUserAspectRatioState.mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN; + return aspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN; } return false; @@ -153,10 +154,12 @@ class AppCompatAspectRatioOverrides { } boolean isSystemOverrideToFullscreenEnabled() { + final int aspectRatio = getUserMinAspectRatioOverrideCode(); + return isChangeEnabled(mActivityRecord, OVERRIDE_ANY_ORIENTATION_TO_USER) && !mAllowOrientationOverrideOptProp.isFalse() - && (mUserAspectRatioState.mUserAspectRatio == USER_MIN_ASPECT_RATIO_UNSET - || mUserAspectRatioState.mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN); + && (aspectRatio == USER_MIN_ASPECT_RATIO_UNSET + || aspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN); } /** @@ -173,12 +176,11 @@ class AppCompatAspectRatioOverrides { } boolean hasFullscreenOverride() { - // `mUserAspectRatio` is always initialized first in `shouldApplyUserFullscreenOverride()`. return shouldApplyUserFullscreenOverride() || isSystemOverrideToFullscreenEnabled(); } float getUserMinAspectRatio() { - switch (mUserAspectRatioState.mUserAspectRatio) { + switch (getUserMinAspectRatioOverrideCode()) { case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: return getDisplaySizeMinAspectRatio(); case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: @@ -269,13 +271,7 @@ class AppCompatAspectRatioOverrides { } int getUserMinAspectRatioOverrideCode() { - try { - return mActivityRecord.mAtmService.getPackageManager() - .getUserMinAspectRatio(mActivityRecord.packageName, mActivityRecord.mUserId); - } catch (RemoteException e) { - Slog.w(TAG, "Exception thrown retrieving aspect ratio user override " + this, e); - } - return mUserAspectRatioState.mUserAspectRatio; + return mUserAspectRatioState.getUserAspectRatio(mActivityRecord); } private float getDefaultMinAspectRatioForUnresizableApps() { @@ -300,10 +296,30 @@ class AppCompatAspectRatioOverrides { } private static class UserAspectRatioState { - // TODO(b/315140179): Make mUserAspectRatio final - // The min aspect ratio override set by user + // The min aspect ratio override set by the user. @PackageManager.UserMinAspectRatio private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET; + private boolean mHasBeenSet = false; + + @PackageManager.UserMinAspectRatio + private int getUserAspectRatio(@NonNull ActivityRecord activityRecord) { + // Package manager can be null at construction time, so access should be on demand. + if (!mHasBeenSet) { + try { + final IPackageManager pm = activityRecord.mAtmService.getPackageManager(); + if (pm != null) { + mUserAspectRatio = pm.getUserMinAspectRatio(activityRecord.packageName, + activityRecord.mUserId); + mHasBeenSet = true; + } + } catch (RemoteException e) { + Slog.w(TAG, "Exception thrown retrieving aspect ratio user override " + + this, e); + } + } + + return mUserAspectRatio; + } } private Resources getResources() { diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java index f5d58eac1113..e3a9d672c17f 100644 --- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java +++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java @@ -56,11 +56,11 @@ class AppCompatOrientationPolicy { final DisplayContent displayContent = mActivityRecord.mDisplayContent; final boolean isIgnoreOrientationRequestEnabled = displayContent != null && displayContent.getIgnoreOrientationRequest(); - final boolean shouldApplyUserFullscreenOverride = mAppCompatOverrides - .getAppCompatAspectRatioOverrides().shouldApplyUserFullscreenOverride(); + final boolean hasFullscreenOverride = mAppCompatOverrides + .getAppCompatAspectRatioOverrides().hasFullscreenOverride(); final boolean shouldCameraCompatControlOrientation = AppCompatCameraPolicy.shouldCameraCompatControlOrientation(mActivityRecord); - if (shouldApplyUserFullscreenOverride && isIgnoreOrientationRequestEnabled + if (hasFullscreenOverride && isIgnoreOrientationRequestEnabled // Do not override orientation to fullscreen for camera activities. // Fixed-orientation activities are rarely tested in other orientations, and it // often results in sideways or stretched previews. As the camera compat treatment @@ -69,8 +69,7 @@ class AppCompatOrientationPolicy { && !shouldCameraCompatControlOrientation) { Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for " + mActivityRecord + " is overridden to " - + screenOrientationToString(SCREEN_ORIENTATION_USER) - + " by user aspect ratio settings."); + + screenOrientationToString(SCREEN_ORIENTATION_USER)); return SCREEN_ORIENTATION_USER; } @@ -101,24 +100,6 @@ class AppCompatOrientationPolicy { return candidate; } - // mUserAspectRatio is always initialized first in shouldApplyUserFullscreenOverride(), - // which will always come first before this check as user override > device - // manufacturer override. - final boolean isSystemOverrideToFullscreenEnabled = mAppCompatOverrides - .getAppCompatAspectRatioOverrides().isSystemOverrideToFullscreenEnabled(); - if (isSystemOverrideToFullscreenEnabled && isIgnoreOrientationRequestEnabled - // Do not override orientation to fullscreen for camera activities. - // Fixed-orientation activities are rarely tested in other orientations, and it - // often results in sideways or stretched previews. As the camera compat treatment - // targets fixed-orientation activities, overriding the orientation disables the - // treatment. - && !shouldCameraCompatControlOrientation) { - Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) - + " for " + mActivityRecord + " is overridden to " - + screenOrientationToString(SCREEN_ORIENTATION_USER)); - return SCREEN_ORIENTATION_USER; - } - final AppCompatOrientationOverrides.OrientationOverridesState capabilityState = mAppCompatOverrides.getAppCompatOrientationOverrides() .mOrientationOverridesState; diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index ccd59969cec8..6cc4b1e6ede9 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -994,7 +994,9 @@ class BackNavigationController { // Ensure the final animation targets which hidden by transition could be visible. for (int i = 0; i < targets.size(); i++) { final WindowContainer wc = targets.get(i).mContainer; - wc.prepareSurfaces(); + if (wc.mSurfaceControl != null) { + wc.prepareSurfaces(); + } } // The pending builder could be cleared due to prepareSurfaces @@ -1328,16 +1330,13 @@ class BackNavigationController { if (!allWindowDrawn) { return; } - final SurfaceControl startingSurface = mOpenAnimAdaptor.mStartingSurface; - if (startingSurface != null && startingSurface.isValid()) { - startTransaction.addTransactionCommittedListener(Runnable::run, () -> { - synchronized (mWindowManagerService.mGlobalLock) { - if (mOpenAnimAdaptor != null) { - mOpenAnimAdaptor.cleanUpWindowlessSurface(true); - } + startTransaction.addTransactionCommittedListener(Runnable::run, () -> { + synchronized (mWindowManagerService.mGlobalLock) { + if (mOpenAnimAdaptor != null) { + mOpenAnimAdaptor.cleanUpWindowlessSurface(true); } - }); - } + } + }); } void clearBackAnimateTarget(boolean cancel) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 41a580438776..6707a27d83c8 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -105,6 +105,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserProperties; import android.content.res.Configuration; import android.graphics.Rect; +import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.power.Mode; @@ -153,6 +154,7 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.WindowManagerPolicy; import com.android.server.utils.Slogf; +import com.android.server.wm.utils.RegionUtils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -270,6 +272,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private boolean mTaskLayersChanged = true; private int mTmpTaskLayerRank; private final RankTaskLayersRunnable mRankTaskLayersRunnable = new RankTaskLayersRunnable(); + private Region mTmpOccludingRegion; + private Region mTmpTaskRegion; private String mDestroyAllActivitiesReason; private final Runnable mDestroyAllActivitiesRunnable = new Runnable() { @@ -2921,6 +2925,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> }); } + void invalidateTaskLayersAndUpdateOomAdjIfNeeded() { + mRankTaskLayersRunnable.mCheckUpdateOomAdj = true; + invalidateTaskLayers(); + } + void invalidateTaskLayers() { if (!mTaskLayersChanged) { mTaskLayersChanged = true; @@ -2938,13 +2947,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Only rank for leaf tasks because the score of activity is based on immediate parent. forAllLeafTasks(task -> { final int oldRank = task.mLayerRank; - final ActivityRecord r = task.topRunningActivityLocked(); - if (r != null && r.isVisibleRequested()) { + final int oldRatio = task.mNonOccludedFreeformAreaRatio; + task.mNonOccludedFreeformAreaRatio = 0; + if (task.isVisibleRequested()) { task.mLayerRank = ++mTmpTaskLayerRank; + if (task.inFreeformWindowingMode()) { + computeNonOccludedFreeformAreaRatio(task); + } } else { task.mLayerRank = Task.LAYER_RANK_INVISIBLE; } - if (task.mLayerRank != oldRank) { + if (task.mLayerRank != oldRank + || task.mNonOccludedFreeformAreaRatio != oldRatio) { task.forAllActivities(activity -> { if (activity.hasProcess()) { mTaskSupervisor.onProcessActivityStateChanged(activity.app, @@ -2953,12 +2967,42 @@ class RootWindowContainer extends WindowContainer<DisplayContent> }); } }, true /* traverseTopToBottom */); - + if (mTmpOccludingRegion != null) { + mTmpOccludingRegion.setEmpty(); + } + boolean changed = false; if (!mTaskSupervisor.inActivityVisibilityUpdate()) { - mTaskSupervisor.computeProcessActivityStateBatch(); + changed = mTaskSupervisor.computeProcessActivityStateBatch(); + } + if (mRankTaskLayersRunnable.mCheckUpdateOomAdj) { + mRankTaskLayersRunnable.mCheckUpdateOomAdj = false; + if (changed) { + mService.updateOomAdj(); + } } } + /** This method is called for visible freeform task from top to bottom. */ + private void computeNonOccludedFreeformAreaRatio(@NonNull Task task) { + if (!com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode()) { + return; + } + if (mTmpOccludingRegion == null) { + mTmpOccludingRegion = new Region(); + mTmpTaskRegion = new Region(); + } + final Rect taskBounds = task.getBounds(); + mTmpTaskRegion.set(taskBounds); + // Exclude the area outside the display. + mTmpTaskRegion.op(task.mDisplayContent.getBounds(), Region.Op.INTERSECT); + // Exclude the area covered by the above tasks. + mTmpTaskRegion.op(mTmpOccludingRegion, Region.Op.DIFFERENCE); + task.mNonOccludedFreeformAreaRatio = 100 * RegionUtils.getAreaSize(mTmpTaskRegion) + / (taskBounds.width() * taskBounds.height()); + // Accumulate the occluding region for other visible tasks behind. + mTmpOccludingRegion.op(taskBounds, Region.Op.UNION); + } + void clearOtherAppTimeTrackers(AppTimeTracker except) { forAllActivities(r -> { if (r.appTimeTracker != except) { @@ -3862,6 +3906,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } private class RankTaskLayersRunnable implements Runnable { + boolean mCheckUpdateOomAdj; + @Override public void run() { synchronized (mService.mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 4861341f830a..8a624b3945b6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -432,6 +432,9 @@ class Task extends TaskFragment { // This number will be assigned when we evaluate OOM scores for all visible tasks. int mLayerRank = LAYER_RANK_INVISIBLE; + /** A 0~100 ratio to indicate the percentage of visible area on screen of a freeform task. */ + int mNonOccludedFreeformAreaRatio; + /* Unique identifier for this task. */ final int mTaskId; /* User for which this task was created. */ @@ -1207,6 +1210,28 @@ class Task extends TaskFragment { } @Override + void onResize() { + super.onResize(); + updateTaskLayerForFreeform(); + } + + @Override + void onMovedByResize() { + super.onMovedByResize(); + updateTaskLayerForFreeform(); + } + + private void updateTaskLayerForFreeform() { + if (!com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode()) { + return; + } + if (!isVisibleRequested() || !inFreeformWindowingMode()) { + return; + } + mRootWindowContainer.invalidateTaskLayersAndUpdateOomAdjIfNeeded(); + } + + @Override @Nullable ActivityRecord getTopResumedActivity() { if (!isLeafTask()) { @@ -3410,6 +3435,36 @@ class Task extends TaskFragment { info.requestedVisibleTypes = (windowState != null && Flags.enableFullyImmersiveInDesktop()) ? windowState.getRequestedVisibleTypes() : WindowInsets.Type.defaultVisible(); AppCompatUtils.fillAppCompatTaskInfo(this, info, top); + info.topActivityMainWindowFrame = calculateTopActivityMainWindowFrameForTaskInfo(top); + } + + /** + * Returns the top activity's main window frame if it doesn't match + * {@link ActivityRecord#getBounds() the top activity bounds}, or {@code null}, otherwise. + * + * @param top The top running activity of the task + */ + @Nullable + private static Rect calculateTopActivityMainWindowFrameForTaskInfo( + @Nullable ActivityRecord top) { + if (!Flags.betterSupportNonMatchParentActivity()) { + return null; + } + if (top == null) { + return null; + } + final WindowState mainWindow = top.findMainWindow(); + if (mainWindow == null) { + return null; + } + if (!mainWindow.mHaveFrame) { + return null; + } + final Rect windowFrame = mainWindow.getFrame(); + if (top.getBounds().equals(windowFrame)) { + return null; + } + return windowFrame; } /** @@ -5919,6 +5974,10 @@ class Task extends TaskFragment { pw.print(prefix); pw.print(" mLastNonFullscreenBounds="); pw.println(mLastNonFullscreenBounds); } + if (mNonOccludedFreeformAreaRatio != 0) { + pw.print(prefix); pw.print(" mNonOccludedFreeformAreaRatio="); + pw.println(mNonOccludedFreeformAreaRatio); + } if (isLeafTask()) { pw.println(prefix + " isSleeping=" + shouldSleepActivities()); printThisActivity(pw, getTopPausingActivity(), dumpPackage, false, diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 3ffeacfd5006..d6ba3123eb97 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -3027,7 +3027,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { // The task order may be changed by finishIfPossible() for adjusting focus if there are // nested tasks, so add all activities into a list to avoid missed removals. final ArrayList<ActivityRecord> removingActivities = new ArrayList<>(); - forAllActivities((Consumer<ActivityRecord>) removingActivities::add); + forAllActivities((r) -> { + if (!r.finishing) { + removingActivities.add(r); + } + }); for (int i = removingActivities.size() - 1; i >= 0; --i) { final ActivityRecord r = removingActivities.get(i); if (withTransition && r.isVisible()) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 1a107c24a16a..7f0c33657144 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -121,6 +121,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio */ private static final int MAX_NUM_PERCEPTIBLE_FREEFORM = SystemProperties.getInt("persist.wm.max_num_perceptible_freeform", 1); + /** + * If the visible area percentage of a resumed freeform task is greater than or equal to this + * ratio, its process will have a higher priority. + */ + private static final int PERCEPTIBLE_FREEFORM_VISIBLE_RATIO = 90; private static final int MAX_RAPID_ACTIVITY_LAUNCH_COUNT = 200; private static final long RAPID_ACTIVITY_LAUNCH_MS = 500; @@ -1234,6 +1239,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio boolean hasResumedFreeform = false; int minTaskLayer = Integer.MAX_VALUE; int stateFlags = 0; + int nonOccludedRatio = 0; final boolean wasResumed = hasResumedActivity(); final boolean wasAnyVisible = (mActivityStateFlags & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0; @@ -1261,6 +1267,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio stateFlags |= ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN; } else if (windowingMode == WINDOWING_MODE_FREEFORM) { hasResumedFreeform = true; + nonOccludedRatio = + Math.max(task.mNonOccludedFreeformAreaRatio, nonOccludedRatio); } } if (minTaskLayer > 0) { @@ -1298,7 +1306,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio && com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode() // Exclude task layer 1 because it is already the top most. && minTaskLayer > 1) { - if (minTaskLayer <= 1 + MAX_NUM_PERCEPTIBLE_FREEFORM) { + if (minTaskLayer <= 1 + MAX_NUM_PERCEPTIBLE_FREEFORM + || nonOccludedRatio >= PERCEPTIBLE_FREEFORM_VISIBLE_RATIO) { stateFlags |= ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM; } else { stateFlags |= ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE; diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java index ce7776f270fd..ff23145fc6b9 100644 --- a/services/core/java/com/android/server/wm/utils/RegionUtils.java +++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java @@ -81,4 +81,15 @@ public class RegionUtils { Collections.reverse(rects); rects.forEach(rectConsumer); } + + /** Returns the area size of the region. */ + public static int getAreaSize(Region region) { + final RegionIterator regionIterator = new RegionIterator(region); + int area = 0; + final Rect rect = new Rect(); + while (regionIterator.next(rect)) { + area += rect.width() * rect.height(); + } + return area; + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0b7ce75136bb..2461c1ce3b0e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -106,6 +106,7 @@ import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.ApplicationSharedMemory; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; +import com.android.internal.pm.RoSystemFeatures; import com.android.internal.policy.AttributeCache; import com.android.internal.protolog.ProtoLog; import com.android.internal.protolog.ProtoLogConfigurationServiceImpl; @@ -1495,8 +1496,7 @@ public final class SystemServer implements Dumpable { boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", false); - boolean isWatch = context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WATCH); + boolean isWatch = RoSystemFeatures.hasFeatureWatch(context); boolean isArc = context.getPackageManager().hasSystemFeature( "org.chromium.arc"); @@ -1504,6 +1504,8 @@ public final class SystemServer implements Dumpable { boolean isTv = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); + boolean isAutomotive = RoSystemFeatures.hasFeatureAutomotive(context); + boolean enableVrService = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE); @@ -1760,7 +1762,8 @@ public final class SystemServer implements Dumpable { t.traceEnd(); } - if (android.security.Flags.aapmApi()) { + if (!isWatch && !isTv && !isAutomotive + && android.security.Flags.aapmApi()) { t.traceBegin("StartAdvancedProtectionService"); mSystemServiceManager.startService(AdvancedProtectionService.Lifecycle.class); t.traceEnd(); @@ -2758,7 +2761,7 @@ public final class SystemServer implements Dumpable { t.traceEnd(); } - if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) { + if (RoSystemFeatures.hasFeatureEmbedded(context)) { t.traceBegin("StartIoTSystemService"); mSystemServiceManager.startService(IOT_SERVICE_CLASS); t.traceEnd(); @@ -3137,8 +3140,6 @@ public final class SystemServer implements Dumpable { }, WEBVIEW_PREPARATION); } - boolean isAutomotive = mPackageManager - .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); if (isAutomotive) { t.traceBegin("StartCarServiceHelperService"); final SystemService cshs = mSystemServiceManager diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp index f15e533fee2b..2f00a1bb3c8c 100644 --- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp +++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp @@ -32,6 +32,7 @@ android_test { "androidx.test.runner", "truth", "Harrier", + "bedstead-multiuser", ], platform_apis: true, certificate: "platform", diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java index 70a2d4847ce7..48cebd7dcb04 100644 --- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java +++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS; import static android.Manifest.permission.MOVE_PACKAGE; import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST; +import static com.android.bedstead.multiuser.MultiUserDeviceStateExtensionsKt.secondaryUser; import static com.android.compatibility.common.util.ShellUtils.runShellCommand; import static com.google.common.truth.Truth.assertThat; @@ -112,9 +113,9 @@ public class CrossUserPackageVisibilityTests { final UserReference primaryUser = sDeviceState.primaryUser(); if (primaryUser.id() == UserHandle.myUserId()) { mCurrentUser = primaryUser; - mOtherUser = sDeviceState.secondaryUser(); + mOtherUser = secondaryUser(sDeviceState); } else { - mCurrentUser = sDeviceState.secondaryUser(); + mCurrentUser = secondaryUser(sDeviceState); mOtherUser = primaryUser; } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index fdf6b809fa85..9189c2f20d66 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -2224,6 +2224,8 @@ public final class DisplayPowerControllerTest { verify(mHolder.animator).animateTo(eq(DEFAULT_DOZE_BRIGHTNESS), /* linearSecondTarget= */ anyFloat(), eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + // This brightness shouldn't be stored in the setting + verify(mHolder.brightnessSetting, never()).setBrightness(DEFAULT_DOZE_BRIGHTNESS); // The display device changes and the default doze brightness changes setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 5c718d982476..b2fe138e3342 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -1726,6 +1726,8 @@ public class QuotaControllerTest { } // Top-started job + mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); + // Top-stared jobs are out of quota enforcement. setProcessState(ActivityManager.PROCESS_STATE_TOP); synchronized (mQuotaController.mLock) { trackJobs(job, jobDefIWF, jobHigh); @@ -1755,6 +1757,38 @@ public class QuotaControllerTest { assertEquals(timeUntilQuotaConsumedMs, mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh)); } + + mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); + // Quota is enforced for top-started job after the process leaves TOP/BTOP state. + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + trackJobs(job, jobDefIWF, jobHigh); + mQuotaController.prepareForExecutionLocked(job); + mQuotaController.prepareForExecutionLocked(jobDefIWF); + mQuotaController.prepareForExecutionLocked(jobHigh); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); + mQuotaController.maybeStopTrackingJobLocked(job, null); + mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null); + mQuotaController.maybeStopTrackingJobLocked(jobHigh, null); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF)); + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh)); + } } @Test @@ -1824,6 +1858,7 @@ public class QuotaControllerTest { mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } + mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); synchronized (mQuotaController.mLock) { @@ -1831,6 +1866,7 @@ public class QuotaControllerTest { } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); synchronized (mQuotaController.mLock) { + // Top-started job is out of quota enforcement. assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); mQuotaController.maybeStopTrackingJobLocked(job, null); @@ -1842,6 +1878,28 @@ public class QuotaControllerTest { mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } + mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); + // Top-started job + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(job); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + // Top-started job is enforced by quota policy after the app leaves the TOP state. + // The max execution time should be the total EJ session limit of the RARE bucket + // minus the time has been used. + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mQuotaController.maybeStopTrackingJobLocked(job, null); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + // Test used quota rolling out of window. synchronized (mQuotaController.mLock) { mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); @@ -1856,6 +1914,7 @@ public class QuotaControllerTest { mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } + mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); synchronized (mQuotaController.mLock) { @@ -1864,6 +1923,7 @@ public class QuotaControllerTest { } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); synchronized (mQuotaController.mLock) { + // Top-started job is out of quota enforcement. assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); mQuotaController.maybeStopTrackingJobLocked(job, null); @@ -1874,6 +1934,28 @@ public class QuotaControllerTest { assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } + + mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); + // Top-started job + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + // Top-started job is enforced by quota policy after the app leaves the TOP state. + // The max execution time should be the total EJ session limit of the RARE bucket. + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mQuotaController.maybeStopTrackingJobLocked(job, null); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } } @Test @@ -1902,6 +1984,7 @@ public class QuotaControllerTest { mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } + mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); synchronized (mQuotaController.mLock) { @@ -1909,6 +1992,7 @@ public class QuotaControllerTest { } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); synchronized (mQuotaController.mLock) { + // Top-started job is out of quota enforcement. assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); mQuotaController.maybeStopTrackingJobLocked(job, null); @@ -1920,6 +2004,27 @@ public class QuotaControllerTest { mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } + mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); + // Top-started job + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(job); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + // Top-started job is enforced by quota policy after the app leaves the TOP state. + // The max execution time should be the total EJ session limit of the RARE bucket. + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mQuotaController.maybeStopTrackingJobLocked(job, null); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + // Test used quota rolling out of window. synchronized (mQuotaController.mLock) { mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); @@ -1935,6 +2040,7 @@ public class QuotaControllerTest { mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } + mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); synchronized (mQuotaController.mLock) { @@ -1943,6 +2049,7 @@ public class QuotaControllerTest { } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); synchronized (mQuotaController.mLock) { + // Top-started job is out of quota enforcement. assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); mQuotaController.maybeStopTrackingJobLocked(job, null); @@ -1953,6 +2060,28 @@ public class QuotaControllerTest { assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } + + mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); + // Top-started job + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + // Top-started job is enforced by quota policy after the app leaves the TOP state. + // The max execution time should be the total EJ session limit of the RARE bucket. + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mQuotaController.maybeStopTrackingJobLocked(job, null); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } } /** @@ -4608,6 +4737,7 @@ public class QuotaControllerTest { assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); advanceElapsedClock(SECOND_IN_MILLIS); + mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); // Bg job starts while inactive, spans an entire active session, and ends after the // active session. @@ -4686,8 +4816,66 @@ public class QuotaControllerTest { mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); mQuotaController.maybeStopTrackingJobLocked(jobFg1, null); } + // jobBg2 and jobFg1 are counted, jobTop is not counted. expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); + + // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to + // foreground_service and a new job starts. Shortly after, uid goes + // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs. + // This should result in two TimingSessions: + // * The first should have a count of 1 + // * The second should have a count of 2, which accounts for the bg2 and fg and top jobs. + // Top started jobs are not quota free any more if the process leaves TOP/BTOP state. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + } + setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + setProcessState(getProcessStateQuotaFreeThreshold()); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg1); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobTop, null); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStopTrackingJobLocked(jobFg1, null); + } + // jobBg2, jobFg1 and jobTop are counted. + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 3)); + assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } /** @@ -4807,7 +4995,8 @@ public class QuotaControllerTest { * Tests that TOP jobs aren't stopped when an app runs out of quota. */ @Test - public void testTracking_OutOfQuota_ForegroundAndBackground() { + @DisableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS) + public void testTracking_OutOfQuota_ForegroundAndBackground_DisableTopStartedJobsThrottling() { setDischarging(); JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1); @@ -4851,6 +5040,7 @@ public class QuotaControllerTest { // Go to a background state. setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); advanceElapsedClock(remainingTimeMs / 2 + 1); + // Only Bg job will be changed from in-quota to out-of-quota. inOrder.verify(mJobSchedulerService, timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) .onControllerStateChanged(argThat(jobs -> jobs.size() == 1)); @@ -4897,6 +5087,105 @@ public class QuotaControllerTest { } /** + * Tests that TOP jobs are stopped when an app runs out of quota. + */ + @Test + @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS) + public void testTracking_OutOfQuota_ForegroundAndBackground_EnableTopStartedJobsThrottling() { + setDischarging(); + + JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1); + JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2); + trackJobs(jobBg, jobTop); + setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window + // Now the package only has 20 seconds to run. + final long remainingTimeMs = 20 * SECOND_IN_MILLIS; + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, + 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false); + + InOrder inOrder = inOrder(mJobSchedulerService); + + // UID starts out inactive. + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + // Start the job. + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg); + } + advanceElapsedClock(remainingTimeMs / 2); + // New job starts after UID is in the foreground. Since the app is now in the foreground, it + // should continue to have remainingTimeMs / 2 time remaining. + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } + advanceElapsedClock(remainingTimeMs); + + // Wait for some extra time to allow for job processing. + inOrder.verify(mJobSchedulerService, + timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) + .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); + synchronized (mQuotaController.mLock) { + assertEquals(remainingTimeMs / 2, + mQuotaController.getRemainingExecutionTimeLocked(jobBg)); + assertEquals(remainingTimeMs / 2, + mQuotaController.getRemainingExecutionTimeLocked(jobTop)); + } + // Go to a background state. + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + advanceElapsedClock(remainingTimeMs / 2 + 1); + // Both Bg and Top jobs should be changed from in-quota to out-of-quota + inOrder.verify(mJobSchedulerService, + timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(argThat(jobs -> jobs.size() == 2)); + // Top job should NOT be allowed to run. + assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertFalse(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + + // New jobs to run. + JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3); + JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4); + JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5); + setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); + + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + // Both Bg and Top jobs should be changed from out-of-quota to in-quota. + inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(argThat(jobs -> jobs.size() == 2)); + trackJobs(jobFg, jobTop); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } + assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + + // App still in foreground so everything should be in quota. + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(getProcessStateQuotaFreeThreshold()); + assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + + advanceElapsedClock(20 * SECOND_IN_MILLIS); + // App is in background so everything should be out of quota. + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + // Bg, Fg and Top jobs should be changed from in-quota to out-of-quota. + inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(argThat(jobs -> jobs.size() == 3)); + // App is now in background and out of quota. Fg should now change to out of quota + // since it wasn't started. Top should now changed to out of quota even it started + // when the app was in TOP. + assertFalse(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + trackJobs(jobBg2); + assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); + } + + /** * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches * its quota. */ @@ -6280,6 +6569,7 @@ public class QuotaControllerTest { mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); advanceElapsedClock(SECOND_IN_MILLIS); + mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to // foreground_service and a new job starts. Shortly after, uid goes @@ -6333,6 +6623,63 @@ public class QuotaControllerTest { expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2)); assertEquals(expected, mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(SECOND_IN_MILLIS); + mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS); + + // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to + // foreground_service and a new job starts. Shortly after, uid goes + // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs. + // This should result in two TimingSessions: + // * The first should have a count of 1 + // * The second should have a count of 3, which accounts for the bg2, fg and top jobs. + // Top started jobs are not quota free any more if the process leaves TOP/BTOP state. + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(jobBg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStartTrackingJobLocked(jobFg1, null); + mQuotaController.maybeStartTrackingJobLocked(jobTop, null); + } + setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg1); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + setProcessState(getProcessStateQuotaFreeThreshold()); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobFg1); + } + advanceElapsedClock(5 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg2); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobTop, null); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(jobBg2, null); + mQuotaController.maybeStopTrackingJobLocked(jobFg1, null); + } + expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 3)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); } /** @@ -6701,7 +7048,8 @@ public class QuotaControllerTest { * Tests that expedited jobs aren't stopped when an app runs out of quota. */ @Test - public void testEJTracking_OutOfQuota_ForegroundAndBackground() { + @DisableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS) + public void testEJTracking_OutOfQuota_ForegroundAndBackground_DisableTopStartedJobsThrottling() { setDischarging(); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0); @@ -6813,6 +7161,129 @@ public class QuotaControllerTest { } /** + * Tests that expedited jobs are stopped when an app runs out of quota. + */ + @Test + @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS) + public void testEJTracking_OutOfQuota_ForegroundAndBackground_EnableTopStartedJobsThrottling() { + setDischarging(); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0); + + JobStatus jobBg = + createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 1); + JobStatus jobTop = + createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 2); + JobStatus jobUnstarted = + createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 3); + trackJobs(jobBg, jobTop, jobUnstarted); + setStandbyBucket(WORKING_INDEX, jobTop, jobBg, jobUnstarted); + // Now the package only has 20 seconds to run. + final long remainingTimeMs = 20 * SECOND_IN_MILLIS; + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession( + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS, + mQcConstants.EJ_LIMIT_WORKING_MS - remainingTimeMs, 1), true); + + InOrder inOrder = inOrder(mJobSchedulerService); + + // UID starts out inactive. + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + // Start the job. + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobBg); + } + advanceElapsedClock(remainingTimeMs / 2); + // New job starts after UID is in the foreground. Since the app is now in the foreground, it + // should continue to have remainingTimeMs / 2 time remaining. + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop); + } + advanceElapsedClock(remainingTimeMs); + + // Wait for some extra time to allow for job processing. + inOrder.verify(mJobSchedulerService, + timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0)) + .onControllerStateChanged(argThat(jobs -> jobs.size() > 0)); + synchronized (mQuotaController.mLock) { + assertEquals(remainingTimeMs / 2, + mQuotaController.getRemainingEJExecutionTimeLocked( + SOURCE_USER_ID, SOURCE_PACKAGE)); + } + // Go to a background state. + setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING); + advanceElapsedClock(remainingTimeMs / 2 + 1); + // Bg, Top and jobUnstarted should be changed from in-quota to out-of-quota. + inOrder.verify(mJobSchedulerService, + timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(argThat(jobs -> jobs.size() == 3)); + // Top should still NOT be "in quota" even it started before the app + // ran on top out of quota. + assertFalse(jobBg.isExpeditedQuotaApproved()); + assertFalse(jobTop.isExpeditedQuotaApproved()); + assertFalse(jobUnstarted.isExpeditedQuotaApproved()); + synchronized (mQuotaController.mLock) { + assertTrue( + 0 >= mQuotaController + .getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + // New jobs to run. + JobStatus jobBg2 = + createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 4); + JobStatus jobTop2 = + createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 5); + JobStatus jobFg = + createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 6); + setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg); + + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + // Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota. + // jobBg, jobFg and jobUnstarted are changed from out-of-quota to in-quota. + inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(argThat(jobs -> jobs.size() == 3)); + trackJobs(jobTop2, jobFg); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(jobTop2); + } + assertTrue(jobTop.isExpeditedQuotaApproved()); + assertTrue(jobTop2.isExpeditedQuotaApproved()); + assertTrue(jobFg.isExpeditedQuotaApproved()); + assertTrue(jobBg.isExpeditedQuotaApproved()); + assertTrue(jobUnstarted.isExpeditedQuotaApproved()); + + // App still in foreground so everything should be in quota. + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(getProcessStateQuotaFreeThreshold()); + assertTrue(jobTop.isExpeditedQuotaApproved()); + assertTrue(jobTop2.isExpeditedQuotaApproved()); + assertTrue(jobFg.isExpeditedQuotaApproved()); + assertTrue(jobBg.isExpeditedQuotaApproved()); + assertTrue(jobUnstarted.isExpeditedQuotaApproved()); + + advanceElapsedClock(20 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + // Bg, Fg, Top, Top2 and jobUnstarted should be changed from in-quota to out-of-quota + inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1)) + .onControllerStateChanged(argThat(jobs -> jobs.size() == 5)); + // App is now in background and out of quota. Fg should now change to out of quota since it + // wasn't started. Top should change to out of quota as the app leaves TOP state. + assertFalse(jobTop.isExpeditedQuotaApproved()); + assertFalse(jobTop2.isExpeditedQuotaApproved()); + assertFalse(jobFg.isExpeditedQuotaApproved()); + assertFalse(jobBg.isExpeditedQuotaApproved()); + trackJobs(jobBg2); + assertFalse(jobBg2.isExpeditedQuotaApproved()); + assertFalse(jobUnstarted.isExpeditedQuotaApproved()); + synchronized (mQuotaController.mLock) { + assertTrue( + 0 >= mQuotaController + .getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + } + + /** * Tests that Timers properly track overlapping top and background jobs. */ @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java index 625dbe6e16f9..bf946a1258fd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java @@ -41,14 +41,19 @@ import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.PlayerProxy; import android.media.audiopolicy.AudioPolicy; +import android.multiuser.Flags; import android.os.Build; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.ArraySet; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -60,7 +65,6 @@ import java.util.List; import java.util.Stack; @RunWith(JUnit4.class) - public class BackgroundUserSoundNotifierTest { private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation() .getTargetContext(); @@ -72,6 +76,10 @@ public class BackgroundUserSoundNotifierTest { @Mock private NotificationManager mNotificationManager; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -142,8 +150,11 @@ public class BackgroundUserSoundNotifierTest { final int fgUserId = mSpiedContext.getUserId(); int bgUserId = fgUserId + 1; int bgUserUid = bgUserId * 100000; - mBackgroundUserSoundNotifier.mNotificationClientUid = bgUserUid; - + if (Flags.multipleAlarmNotificationsSupport()) { + mBackgroundUserSoundNotifier.mNotificationClientUids.add(bgUserUid); + } else { + mBackgroundUserSoundNotifier.mNotificationClientUid = bgUserUid; + } AudioManager mockAudioManager = mock(AudioManager.class); when(mSpiedContext.getSystemService(AudioManager.class)).thenReturn(mockAudioManager); @@ -243,6 +254,72 @@ public class BackgroundUserSoundNotifierTest { notification.actions[0].title); } + @RequiresFlagsEnabled({Flags.FLAG_MULTIPLE_ALARM_NOTIFICATIONS_SUPPORT}) + @Test + public void testMultipleAlarmsSameUid_OneNotificationCreated() throws RemoteException { + assumeTrue(UserManager.supportsMultipleUsers()); + UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0); + final int fgUserId = mSpiedContext.getUserId(); + final int bgUserUid = user.id * 100000; + doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser(); + + AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build(); + AudioFocusInfo afi1 = new AudioFocusInfo(aa, bgUserUid, "", + /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, + AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); + + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi1); + verify(mNotificationManager) + .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), + eq(afi1.getClientUid()), any(Notification.class), + eq(UserHandle.of(fgUserId))); + + AudioFocusInfo afi2 = new AudioFocusInfo(aa, bgUserUid, "", + /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, + AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); + clearInvocations(mNotificationManager); + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi2); + verify(mNotificationManager, never()) + .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), + eq(afi2.getClientUid()), any(Notification.class), + eq(UserHandle.of(fgUserId))); + } + + @RequiresFlagsEnabled({Flags.FLAG_MULTIPLE_ALARM_NOTIFICATIONS_SUPPORT}) + @Test + public void testMultipleAlarmsDifferentUsers_multipleNotificationsCreated() + throws RemoteException { + assumeTrue(UserManager.supportsMultipleUsers()); + UserInfo user1 = createUser("User1", UserManager.USER_TYPE_FULL_SECONDARY, 0); + UserInfo user2 = createUser("User2", UserManager.USER_TYPE_FULL_SECONDARY, 0); + final int fgUserId = mSpiedContext.getUserId(); + final int bgUserUid1 = user1.id * 100000; + final int bgUserUid2 = user2.id * 100000; + doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser(); + + AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build(); + AudioFocusInfo afi1 = new AudioFocusInfo(aa, bgUserUid1, "", + /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, + AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); + + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi1); + verify(mNotificationManager) + .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), + eq(afi1.getClientUid()), any(Notification.class), + eq(UserHandle.of(fgUserId))); + + AudioFocusInfo afi2 = new AudioFocusInfo(aa, bgUserUid2, "", + /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, + AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); + clearInvocations(mNotificationManager); + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi2); + verify(mNotificationManager) + .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), + eq(afi2.getClientUid()), any(Notification.class), + eq(UserHandle.of(fgUserId))); + } + + private UserInfo createUser(String name, String userType, int flags) { UserInfo user = mUserManager.createUser(name, userType, flags); if (user != null) { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java index 177f30a79ebc..0a1fc3184fca 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java @@ -987,5 +987,7 @@ public class BatteryStatsImplTest { BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) .isEqualTo(60000); + + span.close(); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java index 9a64ce19254b..94997b29cdd5 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java @@ -41,7 +41,7 @@ public class BatteryStatsManagerTest { public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Test - public void testBatteryUsageStatsDataConsistency() { + public void testBatteryUsageStatsDataConsistency() throws Exception { BatteryStatsManager bsm = getContext().getSystemService(BatteryStatsManager.class); BatteryUsageStats stats = bsm.getBatteryUsageStats( new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs( @@ -70,5 +70,7 @@ public class BatteryStatsManagerTest { } } } + + stats.close(); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java index 7d2bc178b993..813dd841b2b9 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java @@ -64,7 +64,7 @@ public class BatteryUsageStatsAtomTest { private static final int UID_3 = 4000; @Test - public void testAtom_BatteryUsageStatsPerUid() { + public void testAtom_BatteryUsageStatsPerUid() throws Exception { final BatteryUsageStats bus = buildBatteryUsageStats(); BatteryStatsService.FrameworkStatsLogger statsLogger = mock(BatteryStatsService.FrameworkStatsLogger.class); @@ -72,6 +72,8 @@ public class BatteryUsageStatsAtomTest { List<StatsEvent> actual = new ArrayList<>(); new BatteryStatsService.StatsPerUidLogger(statsLogger).logStats(bus, actual); + bus.close(); + if (DEBUG) { System.out.println(mockingDetails(statsLogger).printInvocations()); } @@ -284,7 +286,7 @@ public class BatteryUsageStatsAtomTest { } @Test - public void testAtom_BatteryUsageStatsAtomsProto() { + public void testAtom_BatteryUsageStatsAtomsProto() throws Exception { final BatteryUsageStats bus = buildBatteryUsageStats(); final byte[] bytes = bus.getStatsProto(); BatteryUsageStatsAtomsProto proto; @@ -347,6 +349,7 @@ public class BatteryUsageStatsAtomTest { // UID_3 - Should be none, since no interesting data (done last for debugging convenience). assertEquals(3, proto.uidBatteryConsumers.length); + bus.close(); } private void assertSameBatteryConsumer(String message, BatteryConsumer consumer, @@ -582,7 +585,7 @@ public class BatteryUsageStatsAtomTest { } @Test - public void testLargeAtomTruncated() { + public void testLargeAtomTruncated() throws Exception { final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], true, false, false, false, 0); // If not truncated, this BatteryUsageStats object would generate a proto buffer @@ -618,6 +621,8 @@ public class BatteryUsageStatsAtomTest { assertThat(bytes.length).isGreaterThan(20000); assertThat(bytes.length).isLessThan(50000); + batteryUsageStats.close(); + BatteryUsageStatsAtomsProto proto; try { proto = BatteryUsageStatsAtomsProto.parseFrom(bytes); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java index e9d95fcbccea..87a26d0c68e7 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java @@ -94,7 +94,7 @@ public class BatteryUsageStatsProviderTest { } @Test - public void test_getBatteryUsageStats() { + public void test_getBatteryUsageStats() throws IOException { final BatteryUsageStats batteryUsageStats = prepareBatteryUsageStats(false); final List<UidBatteryConsumer> uidBatteryConsumers = @@ -121,24 +121,27 @@ public class BatteryUsageStatsProviderTest { assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(12345); assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(180 * MINUTE_IN_MS); + batteryUsageStats.close(); } @Test - public void batteryLevelInfo_charging() { + public void batteryLevelInfo_charging() throws IOException { final BatteryUsageStats batteryUsageStats = prepareBatteryUsageStats(true); assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000.0); assertThat(batteryUsageStats.getChargeTimeRemainingMs()).isEqualTo(1_200_000); + batteryUsageStats.close(); } @Test - public void batteryLevelInfo_onBattery() { + public void batteryLevelInfo_onBattery() throws IOException { final BatteryUsageStats batteryUsageStats = prepareBatteryUsageStats(false); assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000.0); assertThat(batteryUsageStats.getBatteryTimeRemainingMs()).isEqualTo(600_000); + batteryUsageStats.close(); } @Test - public void test_selectPowerComponents() { + public void test_selectPowerComponents() throws IOException { BatteryStatsImpl batteryStats = prepareBatteryStats(false); BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, @@ -163,6 +166,8 @@ public class BatteryUsageStatsProviderTest { assertThat( uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) .isEqualTo(0); + + batteryUsageStats.close(); } private BatteryStatsImpl prepareBatteryStats(boolean plugInAtTheEnd) { @@ -274,7 +279,7 @@ public class BatteryUsageStatsProviderTest { } @Test - public void testWriteAndReadHistory() { + public void testWriteAndReadHistory() throws IOException { MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); synchronized (batteryStats) { batteryStats.setRecordAllHistoryLocked(true); @@ -306,6 +311,8 @@ public class BatteryUsageStatsProviderTest { Parcel in = Parcel.obtain(); batteryUsageStats.writeToParcel(in, 0); + batteryUsageStats.close(); + final byte[] bytes = in.marshall(); Parcel out = Parcel.obtain(); @@ -349,10 +356,12 @@ public class BatteryUsageStatsProviderTest { assertThat(iterator.hasNext()).isFalse(); assertThat(iterator.next()).isNull(); + + unparceled.close(); } @Test - public void testWriteAndReadHistoryTags() { + public void testWriteAndReadHistoryTags() throws IOException { MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); synchronized (batteryStats) { batteryStats.setRecordAllHistoryLocked(true); @@ -400,6 +409,8 @@ public class BatteryUsageStatsProviderTest { assertThat(parcel.dataSize()).isAtMost(128_000); } + batteryUsageStats.close(); + parcel.setDataPosition(0); BatteryUsageStats unparceled = parcel.readParcelable(getClass().getClassLoader(), @@ -461,7 +472,7 @@ public class BatteryUsageStatsProviderTest { } @Test - public void testAggregateBatteryStats() { + public void testAggregateBatteryStats() throws IOException { BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); setTime(5 * MINUTE_IN_MS); @@ -563,6 +574,8 @@ public class BatteryUsageStatsProviderTest { .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) .isWithin(0.1) .of(180.0); + + stats.close(); } @Test @@ -689,6 +702,8 @@ public class BatteryUsageStatsProviderTest { .isEqualTo(60 * MINUTE_IN_MS); assertThat(count[0]).isEqualTo(expectedNumberOfUpdates); + + stats.close(); } private void setTime(long timeMs) { @@ -733,6 +748,7 @@ public class BatteryUsageStatsProviderTest { .isWithin(PRECISION).of(8.33333); assertThat(uid.getConsumedPower(componentId1)) .isWithin(PRECISION).of(8.33333); + stats.close(); return null; }).when(powerStatsStore).storeBatteryUsageStatsAsync(anyLong(), any()); @@ -750,7 +766,7 @@ public class BatteryUsageStatsProviderTest { } @Test - public void testAggregateBatteryStats_incompatibleSnapshot() { + public void testAggregateBatteryStats_incompatibleSnapshot() throws IOException { MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"}); @@ -776,6 +792,8 @@ public class BatteryUsageStatsProviderTest { when(powerStatsStore.loadPowerStatsSpan(1, BatteryUsageStatsSection.TYPE)) .thenReturn(span1); + span1.close(); + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, mock(PowerAttributor.class), mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore, 0, mMockClock); @@ -787,5 +805,7 @@ public class BatteryUsageStatsProviderTest { assertThat(stats.getCustomPowerComponentNames()) .isEqualTo(batteryStats.getCustomEnergyConsumerNames()); assertThat(stats.getStatsDuration()).isEqualTo(1234); + + stats.close(); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java index 52675f6f6cb7..383616eb59eb 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java @@ -323,6 +323,7 @@ public class BatteryUsageStatsRule implements TestRule { } private void before() { + BatteryUsageStats.DEBUG_INSTANCE_COUNT = true; HandlerThread bgThread = new HandlerThread("bg thread"); bgThread.setUncaughtExceptionHandler((thread, throwable)-> { mThrowable = throwable; @@ -338,6 +339,10 @@ public class BatteryUsageStatsRule implements TestRule { private void after() throws Throwable { waitForBackgroundThread(); + if (mBatteryUsageStats != null) { + mBatteryUsageStats.close(); + } + BatteryUsageStats.assertAllInstancesClosed(); } public void waitForBackgroundThread() throws Throwable { @@ -409,6 +414,14 @@ public class BatteryUsageStatsRule implements TestRule { } BatteryUsageStats apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) { + if (mBatteryUsageStats != null) { + try { + mBatteryUsageStats.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + mBatteryUsageStats = null; + } final String[] customPowerComponentNames = mBatteryStats.getCustomEnergyConsumerNames(); final boolean includePowerModels = (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java index 88624f28a2e5..1b6b8c49461e 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java @@ -76,22 +76,25 @@ public class BatteryUsageStatsTest { private static final int APP_UID2 = 314; @Test - public void testBuilder() { + public void testBuilder() throws Exception { BatteryUsageStats batteryUsageStats = buildBatteryUsageStats1(true).build(); assertBatteryUsageStats1(batteryUsageStats, true); + batteryUsageStats.close(); } @Test - public void testBuilder_noProcessStateData() { + public void testBuilder_noProcessStateData() throws Exception { BatteryUsageStats batteryUsageStats = buildBatteryUsageStats1(false).build(); assertBatteryUsageStats1(batteryUsageStats, false); + batteryUsageStats.close(); } @Test - public void testParcelability_smallNumberOfUids() { + public void testParcelability_smallNumberOfUids() throws Exception { final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats1(true).build(); final Parcel parcel = Parcel.obtain(); parcel.writeParcelable(outBatteryUsageStats, 0); + outBatteryUsageStats.close(); assertThat(parcel.dataSize()).isLessThan(100000); @@ -101,10 +104,11 @@ public class BatteryUsageStatsTest { parcel.readParcelable(getClass().getClassLoader()); assertThat(inBatteryUsageStats).isNotNull(); assertBatteryUsageStats1(inBatteryUsageStats, true); + inBatteryUsageStats.close(); } @Test - public void testParcelability_largeNumberOfUids() { + public void testParcelability_largeNumberOfUids() throws Exception { final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0]); @@ -141,22 +145,27 @@ public class BatteryUsageStatsTest { assertThat(uidBatteryConsumer).isNotNull(); assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(i * 100); } + inBatteryUsageStats.close(); + outBatteryUsageStats.close(); } @Test - public void testDefaultSessionDuration() { + public void testDefaultSessionDuration() throws Exception { final BatteryUsageStats stats = buildBatteryUsageStats1(true).setStatsDuration(10000).build(); assertThat(stats.getStatsDuration()).isEqualTo(10000); + stats.close(); } @Test - public void testDump() { + public void testDump() throws Exception { final BatteryUsageStats stats = buildBatteryUsageStats1(true).build(); final StringWriter out = new StringWriter(); try (PrintWriter pw = new PrintWriter(out)) { stats.dump(pw, " "); } + stats.close(); + final String dump = out.toString(); assertThat(dump).contains("Capacity: 4000"); @@ -187,12 +196,14 @@ public class BatteryUsageStatsTest { } @Test - public void testDumpNoScreenOrPowerState() { + public void testDumpNoScreenOrPowerState() throws Exception { final BatteryUsageStats stats = buildBatteryUsageStats1(true, false, false).build(); final StringWriter out = new StringWriter(); try (PrintWriter pw = new PrintWriter(out)) { stats.dump(pw, " "); } + stats.close(); + final String dump = out.toString(); assertThat(dump).contains("Capacity: 4000"); @@ -222,7 +233,7 @@ public class BatteryUsageStatsTest { } @Test - public void testAdd() { + public void testAdd() throws Exception { final BatteryUsageStats stats1 = buildBatteryUsageStats1(false).build(); final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[]{"FOO"}, true).build(); final BatteryUsageStats sum = @@ -261,24 +272,31 @@ public class BatteryUsageStatsTest { assertAggregateBatteryConsumer(sum, BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 40211, 40422, 40633, 40844); + stats1.close(); + stats2.close(); + sum.close(); } @Test - public void testAdd_customComponentMismatch() { + public void testAdd_customComponentMismatch() throws Exception { final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0); final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"BAR"}, false).build(); assertThrows(IllegalArgumentException.class, () -> builder.add(stats)); + stats.close(); + builder.discard(); } @Test - public void testAdd_processStateDataMismatch() { + public void testAdd_processStateDataMismatch() throws Exception { final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0); final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"FOO"}, false).build(); assertThrows(IllegalArgumentException.class, () -> builder.add(stats)); + stats.close(); + builder.discard(); } @Test @@ -290,12 +308,14 @@ public class BatteryUsageStatsTest { final BatteryUsageStats stats = buildBatteryUsageStats1(true).build(); stats.writeXml(serializer); serializer.endDocument(); + stats.close(); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); TypedXmlPullParser parser = Xml.newBinaryPullParser(); parser.setInput(in, StandardCharsets.UTF_8.name()); final BatteryUsageStats fromXml = BatteryUsageStats.createFromXml(parser); assertBatteryUsageStats1(fromXml, true); + fromXml.close(); } private BatteryUsageStats.Builder buildBatteryUsageStats1(boolean includeUserBatteryConsumer) { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java index 1e4454c9a3f0..b374a3202fa2 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java @@ -17,6 +17,7 @@ package com.android.server.power.stats; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.usage.NetworkStatsManager; @@ -80,7 +81,7 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { Handler handler, PowerStatsUidResolver powerStatsUidResolver) { super(config, clock, new MonotonicClock(0, clock), historyDirectory, handler, mock(PlatformIdleStateCallback.class), mock(EnergyStatsRetriever.class), - mock(UserInfoProvider.class), mock(PowerProfile.class), + mock(UserInfoProvider.class), mockPowerProfile(), new CpuScalingPolicies(new SparseArray<>(), new SparseArray<>()), powerStatsUidResolver, mock(FrameworkStatsLogger.class), mock(BatteryStatsHistory.TraceDelegate.class), @@ -96,6 +97,12 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { mKernelWakelockReader = null; } + private static PowerProfile mockPowerProfile() { + PowerProfile powerProfile = mock(PowerProfile.class); + when(powerProfile.getNumDisplays()).thenReturn(1); + return powerProfile; + } + public void initMeasuredEnergyStats(String[] customBucketNames) { final boolean[] supportedStandardBuckets = new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS]; diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java index 7aa2e0f609a7..2b55303bd89f 100644 --- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java +++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java @@ -19,6 +19,9 @@ package com.android.server.security.forensic; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; import android.annotation.SuppressLint; import android.content.Context; @@ -50,7 +53,8 @@ public class ForensicServiceTest { IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; @Mock - private Context mContextSpy; + private Context mContext; + private BackupTransportConnection mBackupTransportConnection; private ForensicService mForensicService; private TestLooper mTestLooper; @@ -63,7 +67,7 @@ public class ForensicServiceTest { mTestLooper = new TestLooper(); mLooper = mTestLooper.getLooper(); - mForensicService = new ForensicService(new MockInjector(mContextSpy)); + mForensicService = new ForensicService(new MockInjector(mContext)); mForensicService.onStart(); } @@ -253,6 +257,8 @@ public class ForensicServiceTest { assertEquals(STATE_VISIBLE, scb1.mState); assertEquals(STATE_VISIBLE, scb2.mState); + doReturn(true).when(mBackupTransportConnection).initialize(); + CommandCallback ccb = new CommandCallback(); mForensicService.getBinderService().enable(ccb); mTestLooper.dispatchAll(); @@ -262,6 +268,29 @@ public class ForensicServiceTest { } @Test + public void testEnable_FromVisible_TwoMonitors_BackupTransportUnavailable() + throws RemoteException { + mForensicService.setState(STATE_VISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + + doReturn(false).when(mBackupTransportConnection).initialize(); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().enable(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_VISIBLE, scb1.mState); + assertEquals(STATE_VISIBLE, scb2.mState); + assertNotNull(ccb.mErrorCode); + assertEquals(ERROR_BACKUP_TRANSPORT_UNAVAILABLE, ccb.mErrorCode.intValue()); + } + + @Test public void testEnable_FromEnabled_TwoMonitors() throws RemoteException { mForensicService.setState(STATE_ENABLED); StateCallback scb1 = new StateCallback(); @@ -330,6 +359,8 @@ public class ForensicServiceTest { assertEquals(STATE_ENABLED, scb1.mState); assertEquals(STATE_ENABLED, scb2.mState); + doNothing().when(mBackupTransportConnection).release(); + CommandCallback ccb = new CommandCallback(); mForensicService.getBinderService().disable(ccb); mTestLooper.dispatchAll(); @@ -356,6 +387,12 @@ public class ForensicServiceTest { return mLooper; } + @Override + public BackupTransportConnection getBackupTransportConnection() { + mBackupTransportConnection = spy(new BackupTransportConnection(mContext)); + return mBackupTransportConnection; + } + } private static class StateCallback extends IForensicServiceStateCallback.Stub { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java index 7829fcc4b44d..8df18a8c178b 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java @@ -480,7 +480,7 @@ public class MagnificationProcessorTest { if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) { mFullScreenMagnificationControllerStub.resetAndStubMethods(); mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(), - config.getCenterX(), config.getCenterY(), false, SERVICE_ID); + config.getCenterX(), config.getCenterY(), true, false, SERVICE_ID); mMagnificationManagerStub.deactivateIfNeed(); } else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) { mMagnificationManagerStub.resetAndStubMethods(); @@ -531,6 +531,9 @@ public class MagnificationProcessorTest { }; doAnswer(enableMagnificationStubAnswer).when( mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(), + anyFloat(), anyFloat(), anyBoolean(), anyBoolean(), eq(SERVICE_ID)); + doAnswer(enableMagnificationStubAnswer).when( + mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(), anyFloat(), anyFloat(), anyBoolean(), eq(SERVICE_ID)); Answer disableMagnificationStubAnswer = invocation -> { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index c4b4afd13a60..5985abc344a2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; +import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER; import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY; import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; @@ -76,6 +77,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.Flags; import com.android.server.accessibility.test.MessageCapturingHandler; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; @@ -126,6 +128,7 @@ public class FullScreenMagnificationControllerTest { final Resources mMockResources = mock(Resources.class); final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class); final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class); + final InputManagerInternal mMockInputManager = mock(InputManagerInternal.class); private final MagnificationAnimationCallback mAnimationCallback = mock( MagnificationAnimationCallback.class); private final MagnificationInfoChangedCallback mRequestObserver = mock( @@ -163,6 +166,7 @@ public class FullScreenMagnificationControllerTest { when(mMockControllerCtx.getContext()).thenReturn(mMockContext); when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager); when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager); + when(mMockControllerCtx.getInputManager()).thenReturn(mMockInputManager); when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler); when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L); mResolver = new MockContentResolver(); @@ -285,10 +289,11 @@ public class FullScreenMagnificationControllerTest { mFullScreenMagnificationController.magnificationRegionContains(displayId, 100, 100)); assertFalse(mFullScreenMagnificationController.reset(displayId, true)); - assertFalse(mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, 0)); + assertFalse( + mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, true, 0)); assertFalse(mFullScreenMagnificationController.setCenter(displayId, 100, 100, false, 1)); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, - 1.5f, 100, 100, false, 2)); + 1.5f, 100, 100, true, false, 2)); assertTrue(mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0); mFullScreenMagnificationController.getMagnificationRegion(displayId, new Region()); @@ -313,7 +318,7 @@ public class FullScreenMagnificationControllerTest { final float scale = 2.0f; final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER; assertFalse(mFullScreenMagnificationController - .setScale(TEST_DISPLAY, scale, center.x, center.y, false, SERVICE_ID_1)); + .setScale(TEST_DISPLAY, scale, center.x, center.y, true, false, SERVICE_ID_1)); assertFalse(mFullScreenMagnificationController.isActivated(TEST_DISPLAY)); } @@ -331,7 +336,7 @@ public class FullScreenMagnificationControllerTest { final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER; final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale); assertTrue(mFullScreenMagnificationController - .setScale(displayId, scale, center.x, center.y, false, SERVICE_ID_1)); + .setScale(displayId, scale, center.x, center.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets); @@ -357,7 +362,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1)); + .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); // New center should be halfway between original center and pivot @@ -405,7 +410,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; assertTrue(mFullScreenMagnificationController.setScale(displayId, scale, INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(), - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; @@ -440,7 +445,7 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, - newCenter.x, newCenter.y, mAnimationCallback, SERVICE_ID_1)); + newCenter.x, newCenter.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -486,11 +491,11 @@ public class FullScreenMagnificationControllerTest { final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; final float targetScale = 2.0f; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - targetScale, center.x, center.y, false, SERVICE_ID_1)); + targetScale, center.x, center.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, - targetScale, center.x, center.y, mAnimationCallback, SERVICE_ID_1)); + targetScale, center.x, center.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); verify(mMockValueAnimator, never()).start(); @@ -516,7 +521,7 @@ public class FullScreenMagnificationControllerTest { assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, MagnificationScaleProvider.MAX_SCALE + 1.0f, - newCenter.x, newCenter.y, false, SERVICE_ID_1)); + newCenter.x, newCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -527,7 +532,7 @@ public class FullScreenMagnificationControllerTest { // Verify that we can't zoom below 1x assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, 0.5f, INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y, - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, @@ -551,7 +556,7 @@ public class FullScreenMagnificationControllerTest { // Off the edge to the top and left assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, -100f, -200f, false, SERVICE_ID_1)); + scale, -100f, -200f, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; @@ -565,7 +570,7 @@ public class FullScreenMagnificationControllerTest { // Off the edge to the bottom and right assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1, - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); @@ -619,7 +624,7 @@ public class FullScreenMagnificationControllerTest { PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale); // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockWindowManager); @@ -673,7 +678,7 @@ public class FullScreenMagnificationControllerTest { // Upper left edges PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, false, + .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); MagnificationSpec ulSpec = getCurrentMagnificationSpec(displayId); @@ -685,7 +690,7 @@ public class FullScreenMagnificationControllerTest { // Lower right edges PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, false, + .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); MagnificationSpec lrSpec = getCurrentMagnificationSpec(displayId); @@ -710,7 +715,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); @@ -753,7 +758,7 @@ public class FullScreenMagnificationControllerTest { PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale); // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); @@ -786,12 +791,12 @@ public class FullScreenMagnificationControllerTest { register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; assertTrue(mFullScreenMagnificationController - .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false, + .setScale(displayId, 2.0f, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); assertEquals(SERVICE_ID_1, mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId)); assertTrue(mFullScreenMagnificationController - .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false, + .setScale(displayId, 1.5f, startCenter.x, startCenter.y, true, false, SERVICE_ID_2)); assertEquals(SERVICE_ID_2, mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId)); @@ -809,10 +814,10 @@ public class FullScreenMagnificationControllerTest { register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; mFullScreenMagnificationController - .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false, + .setScale(displayId, 2.0f, startCenter.x, startCenter.y, true, false, SERVICE_ID_1); mFullScreenMagnificationController - .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false, + .setScale(displayId, 1.5f, startCenter.x, startCenter.y, true, false, SERVICE_ID_2); assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1)); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId); @@ -873,7 +878,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.5f; PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, firstCenter.x, firstCenter.y, mAnimationCallback, SERVICE_ID_1)); + scale, firstCenter.x, firstCenter.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockValueAnimator); // Stubs the logic after the animation is started. @@ -1076,7 +1081,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; // setting animate parameter to true is differ from zoomIn2xToMiddle() mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - true, SERVICE_ID_1); + true, true, SERVICE_ID_1); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); Mockito.reset(mMockWindowManager); @@ -1107,7 +1112,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec))); @@ -1148,7 +1153,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - true, SERVICE_ID_1); + true, true, SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); when(mMockValueAnimator.isRunning()).thenReturn(true); @@ -1335,7 +1340,7 @@ public class FullScreenMagnificationControllerTest { scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale)); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, firstCenter.x, firstCenter.y, true, SERVICE_ID_1)); + scale, firstCenter.x, firstCenter.y, true, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(firstCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -1404,7 +1409,7 @@ public class FullScreenMagnificationControllerTest { register(DISPLAY_0); final float scale = 1.0f; mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, scale, Float.NaN, Float.NaN, true, SERVICE_ID_1); + DISPLAY_0, scale, Float.NaN, Float.NaN, true, true, SERVICE_ID_1); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ false, DISPLAY_0); verify(mMockWindowManager).setFullscreenMagnificationActivated(DISPLAY_0, true); @@ -1449,7 +1454,7 @@ public class FullScreenMagnificationControllerTest { PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(TEST_DISPLAY, 1.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(TEST_DISPLAY); // persistScale may post a task to a background thread. Let's wait for it completes. @@ -1464,10 +1469,10 @@ public class FullScreenMagnificationControllerTest { register(DISPLAY_1); final PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(DISPLAY_0, 3.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(DISPLAY_0); mFullScreenMagnificationController.setScale(DISPLAY_1, 4.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(DISPLAY_1); // persistScale may post a task to a background thread. Let's wait for it completes. @@ -1479,6 +1484,101 @@ public class FullScreenMagnificationControllerTest { } @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void persistScale_setValue_notifyInput() { + register(TEST_DISPLAY); + + PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y, + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + verify(mMockInputManager, never()).setAccessibilityPointerIconScaleFactor(anyInt(), + anyFloat()); + + mFullScreenMagnificationController.persistScale(TEST_DISPLAY); + + // persistScale may post a task to a background thread. Let's wait for it completes. + waitForBackgroundThread(); + Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(TEST_DISPLAY), + 4.0f); + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScale_setNonTransientScale_notifyInput() { + register(TEST_DISPLAY); + + PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y, + /* isScaleTransient= */ false, /* animate= */ false, SERVICE_ID_1); + + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScaleAndCenter_setTransientScale_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 3.0f, point.x, + point.y, /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + + verify(mRequestObserver).onFullScreenMagnificationChanged(anyInt(), any(Region.class), + any(MagnificationConfig.class)); + verify(mMockInputManager, never()).setAccessibilityPointerIconScaleFactor(anyInt(), + anyFloat()); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScaleAndCenter_setNonTransientScale_notifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 3.0f, point.x, + point.y, /* isScaleTransient= */ false, /* animate= */ false, SERVICE_ID_1); + + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 3.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setCenter_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 2.0f, point.x, point.y, + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + mFullScreenMagnificationController.setCenter(TEST_DISPLAY, point.x, point.y, false, + SERVICE_ID_1); + + // Note that setCenter doesn't change scale, so it's not necessary to notify the input + // manager, but we currently do. The input manager skips redundant computation if the + // notified scale is the same as the previous call. + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, + 2.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void offsetMagnifiedRegion_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 2.0f, point.x, point.y, + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + mFullScreenMagnificationController.offsetMagnifiedRegion(TEST_DISPLAY, 100, 50, + SERVICE_ID_1); + + // Note that setCenter doesn't change scale, so it's not necessary to notify the input + // manager, but we currently do. The input manager skips redundant computation if the + // notified scale is the same as the previous call. + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, + 2.0f); + } + + @Test public void testOnContextChanged_alwaysOnFeatureDisabled_resetMagnification() { setScaleToMagnifying(); @@ -1535,7 +1635,7 @@ public class FullScreenMagnificationControllerTest { PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(DISPLAY_0, scale, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); } private void initMockWindowManager() { @@ -1578,7 +1678,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index e5831b326de5..9f5dd93054c3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -90,6 +90,7 @@ import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.Flags; import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; +import com.android.server.input.InputManagerInternal; import com.android.server.testutils.OffsettableClock; import com.android.server.testutils.TestHandler; import com.android.server.wm.WindowManagerInternal; @@ -227,9 +228,11 @@ public class FullScreenMagnificationGestureHandlerTest { final FullScreenMagnificationController.ControllerContext mockController = mock(FullScreenMagnificationController.ControllerContext.class); final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class); + final InputManagerInternal mockInputManager = mock(InputManagerInternal.class); when(mockController.getContext()).thenReturn(mContext); when(mockController.getTraceManager()).thenReturn(mMockTraceManager); when(mockController.getWindowManager()).thenReturn(mockWindowManager); + when(mockController.getInputManager()).thenReturn(mockInputManager); when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper())); when(mockController.newValueAnimator()).thenReturn(new ValueAnimator()); when(mockController.getAnimationDuration()).thenReturn(1000L); @@ -1343,7 +1346,7 @@ public class FullScreenMagnificationGestureHandlerTest { Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale, UserHandle.USER_SYSTEM); mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1364,7 +1367,7 @@ public class FullScreenMagnificationGestureHandlerTest { Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale, UserHandle.USER_SYSTEM); mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1401,7 +1404,7 @@ public class FullScreenMagnificationGestureHandlerTest { mFullScreenMagnificationController.getPersistedScale(DISPLAY_0); mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1438,7 +1441,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 5.6f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); @@ -1530,7 +1533,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 6.2f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = mouseEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, InputDevice.SOURCE_MOUSE); fastForward(20); @@ -1571,7 +1574,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 4.0f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); // HOVER_MOVE should change magnifier viewport. MotionEvent event = motionEvent(centerX + 20, centerY, ACTION_HOVER_MOVE); @@ -1615,7 +1618,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 5.3f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, source); fastForward(20); @@ -1649,7 +1652,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 2.7f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, source); fastForward(20); @@ -1685,7 +1688,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 3.8f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); @@ -1722,7 +1725,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 4.0f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 25281774cd95..8164ef9314d9 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -76,6 +76,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.window.flags.Flags; @@ -154,6 +155,8 @@ public class MagnificationControllerTest { private WindowManagerInternal mWindowManagerInternal; @Mock private WindowManagerInternal.AccessibilityControllerInternal mA11yController; + @Mock + private InputManagerInternal mInputManagerInternal; @Mock private DisplayManagerInternal mDisplayManagerInternal; @@ -200,6 +203,7 @@ public class MagnificationControllerTest { when(mControllerCtx.getContext()).thenReturn(mContext); when(mControllerCtx.getTraceManager()).thenReturn(mTraceManager); when(mControllerCtx.getWindowManager()).thenReturn(mWindowManagerInternal); + when(mControllerCtx.getInputManager()).thenReturn(mInputManagerInternal); when(mControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler); when(mControllerCtx.getAnimationDuration()).thenReturn(1000L); when(mControllerCtx.newValueAnimator()).thenReturn(mValueAnimator); @@ -417,7 +421,7 @@ public class MagnificationControllerTest { assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, - true, MAGNIFICATION_GESTURE_HANDLER_ID); + true, true, MAGNIFICATION_GESTURE_HANDLER_ID); verify(mTransitionCallBack).onResult(TEST_DISPLAY, false); } @@ -467,7 +471,7 @@ public class MagnificationControllerTest { assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y), - any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID)); + eq(false), any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID)); } @Test @@ -484,7 +488,7 @@ public class MagnificationControllerTest { verify(mScreenMagnificationController, never()).setScaleAndCenter(anyInt(), anyFloat(), anyFloat(), anyFloat(), - anyBoolean(), anyInt()); + anyBoolean(), anyBoolean(), anyInt()); } @Test @@ -546,7 +550,7 @@ public class MagnificationControllerTest { config, animate, TEST_SERVICE_ID); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), /* scale= */ anyFloat(), /* centerX= */ anyFloat(), /* centerY= */ anyFloat(), - mCallbackArgumentCaptor.capture(), /* id= */ anyInt()); + anyBoolean(), mCallbackArgumentCaptor.capture(), /* id= */ anyInt()); mCallbackArgumentCaptor.getValue().onResult(true); mMockConnection.invokeCallbacks(); @@ -616,7 +620,7 @@ public class MagnificationControllerTest { @Test public void magnifyThroughExternalRequest_showMagnificationButton() { mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, - MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, false, TEST_SERVICE_ID); + MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, true, false, TEST_SERVICE_ID); // The first time is trigger when fullscreen mode is activated. // The second time is triggered when magnification spec is changed. @@ -638,7 +642,7 @@ public class MagnificationControllerTest { mMagnificationController.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(newScale), - anyFloat(), anyFloat(), anyBoolean(), anyInt()); + anyFloat(), anyFloat(), anyBoolean(), anyBoolean(), anyInt()); verify(mScreenMagnificationController).persistScale(eq(TEST_DISPLAY)); } @@ -681,7 +685,7 @@ public class MagnificationControllerTest { final MagnificationConfig config = obtainMagnificationConfig(MODE_FULLSCREEN); mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, config.getScale(), config.getCenterX(), config.getCenterY(), - true, TEST_SERVICE_ID); + true, true, TEST_SERVICE_ID); // The notify method is triggered when setting magnification enabled. // The setScaleAndCenter call should not trigger notify method due to same scale and center. @@ -930,7 +934,7 @@ public class MagnificationControllerTest { public void onWindowModeActivated_fullScreenIsActivatedByExternal_fullScreenIsDisabled() { mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, - true, TEST_SERVICE_ID); + true, true, TEST_SERVICE_ID); mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true); @@ -1317,7 +1321,8 @@ public class MagnificationControllerTest { } if (mode == MODE_FULLSCREEN) { mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX, - centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); + centerY, true, true, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); } else { mMagnificationConnectionManager.enableWindowMagnification(displayId, DEFAULT_SCALE, centerX, centerY, null, TEST_SERVICE_ID); diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index 9c6412b81b34..a2e6d4c7bfed 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -191,98 +191,6 @@ public class AppIntegrityManagerServiceImplTest { } @Test - public void updateRuleSet_notAuthorized() throws Exception { - makeUsSystemApp(); - Rule rule = - new Rule( - new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), - Rule.DENY); - TestUtils.assertExpectException( - SecurityException.class, - "Only system packages specified in config_integrityRuleProviderPackages are" - + " allowed to call this method.", - () -> - mService.updateRuleSet( - VERSION, - new ParceledListSlice<>(Arrays.asList(rule)), - /* statusReceiver= */ null)); - } - - @Test - public void updateRuleSet_notSystemApp() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(false); - Rule rule = - new Rule( - new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), - Rule.DENY); - TestUtils.assertExpectException( - SecurityException.class, - "Only system packages specified in config_integrityRuleProviderPackages are" - + " allowed to call this method.", - () -> - mService.updateRuleSet( - VERSION, - new ParceledListSlice<>(Arrays.asList(rule)), - /* statusReceiver= */ null)); - } - - @Test - public void updateRuleSet_authorized() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - Rule rule = - new Rule( - new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), - Rule.DENY); - - // no SecurityException - mService.updateRuleSet( - VERSION, new ParceledListSlice<>(Arrays.asList(rule)), mock(IntentSender.class)); - } - - @Test - public void updateRuleSet_correctMethodCall() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - IntentSender mockReceiver = mock(IntentSender.class); - List<Rule> rules = - Arrays.asList( - new Rule( - IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), - Rule.DENY)); - - mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); - runJobInHandler(); - - verify(mIntegrityFileManager).writeRules(VERSION, TEST_FRAMEWORK_PACKAGE, rules); - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mockReceiver).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any()); - assertEquals(STATUS_SUCCESS, intentCaptor.getValue().getIntExtra(EXTRA_STATUS, -1)); - } - - @Test - public void updateRuleSet_fail() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - doThrow(new IOException()).when(mIntegrityFileManager).writeRules(any(), any(), any()); - IntentSender mockReceiver = mock(IntentSender.class); - List<Rule> rules = - Arrays.asList( - new Rule( - IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), - Rule.DENY)); - - mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); - runJobInHandler(); - - verify(mIntegrityFileManager).writeRules(VERSION, TEST_FRAMEWORK_PACKAGE, rules); - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mockReceiver).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any()); - assertEquals(STATUS_FAILURE, intentCaptor.getValue().getIntExtra(EXTRA_STATUS, -1)); - } - - @Test public void broadcastReceiverRegistration() throws Exception { allowlistUsAsRuleProvider(); makeUsSystemApp(); 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 592eec539ae6..bc01fc4f29c0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java @@ -260,9 +260,10 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { } private void initAttentionHelper(TestableFlagResolver flagResolver) { - mAttentionHelper = new NotificationAttentionHelper(getContext(), mock(LightsManager.class), - mAccessibilityManager, mPackageManager, mUserManager, mUsageStats, - mService.mNotificationManagerPrivate, mock(ZenModeHelper.class), flagResolver); + mAttentionHelper = new NotificationAttentionHelper(getContext(), new Object(), + mock(LightsManager.class),mAccessibilityManager, mPackageManager, + mUserManager, mUsageStats, mService.mNotificationManagerPrivate, + mock(ZenModeHelper.class), flagResolver); mAttentionHelper.onSystemReady(); mAttentionHelper.setVibratorHelper(spy(new VibratorHelper(getContext()))); mAttentionHelper.setAudioManager(mAudioManager); diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java index ff2ce15a7946..6dba96766b48 100644 --- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java +++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java @@ -41,6 +41,8 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Locale; @RunWith(AndroidJUnit4.class) @@ -62,18 +64,23 @@ public class ConversionUtilTest { final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION; final var data = new byte[] {0x11, 0x22}; - final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2]; - keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99, + final var keyphrases = new ArrayList<SoundTrigger.KeyphraseRecognitionExtra>(2); + keyphrases.add(new SoundTrigger.KeyphraseRecognitionExtra(99, RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13, new ConfidenceLevel[] {new ConfidenceLevel(9999, 50), - new ConfidenceLevel(5000, 80)}); - keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101, + new ConfidenceLevel(5000, 80)})); + keyphrases.add(new SoundTrigger.KeyphraseRecognitionExtra(101, RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] { new ConfidenceLevel(7777, 30), - new ConfidenceLevel(2222, 60)}); + new ConfidenceLevel(2222, 60)})); - var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/, - keyphrases, data, flags); + var apiconfig = new SoundTrigger.RecognitionConfig.Builder() + .setCaptureRequested(true) + .setAllowMultipleTriggers(false) // must be false + .setKeyphrases(keyphrases) + .setData(data) + .setAudioCapabilities(flags) + .build(); assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig))); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index c30b4bb6f65d..c3466b9cee18 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -139,7 +139,6 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; -import android.platform.test.annotations.RequiresFlagsEnabled; import android.provider.DeviceConfig; import android.util.MutableBoolean; import android.view.DisplayInfo; @@ -2662,8 +2661,11 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test - @RequiresFlagsEnabled(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT) public void testSetOrientation_restrictedByTargetSdk() { + mSetFlagsRule.enableFlags(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT); + mDisplayContent.setIgnoreOrientationRequest(true); + makeDisplayLargeScreen(mDisplayContent); + assertSetOrientation(Build.VERSION_CODES.CUR_DEVELOPMENT, CATEGORY_SOCIAL, false); assertSetOrientation(Build.VERSION_CODES.CUR_DEVELOPMENT, CATEGORY_GAME, true); @@ -2673,12 +2675,13 @@ public class ActivityRecordTests extends WindowTestsBase { } private void assertSetOrientation(int targetSdk, int category, boolean expectRotate) { - final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); - activity.mTargetSdk = targetSdk; + final String packageName = targetSdk <= Build.VERSION_CODES.VANILLA_ICE_CREAM + ? mContext.getPackageName() // WmTests uses legacy sdk. + : null; // Simulate CUR_DEVELOPMENT by invalid package (see PlatformCompat). + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true) + .setComponent(getUniqueComponentName(packageName)).build(); activity.info.applicationInfo.category = category; - activity.setVisible(true); - // Assert orientation is unspecified to start. assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.getOrientation()); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java index c8a35598479f..08963f1c7647 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java @@ -307,6 +307,8 @@ class AppCompatActivityRobot { void createNewTaskWithBaseActivity() { final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor) .setCreateActivity(true) + // Respect "@ChangeId" according to test package's target sdk. + .setPackage(mAtm.mContext.getPackageName()) .setDisplay(mDisplayContent).build(); mTaskStack.push(newTask); pushActivity(newTask.getTopNonFinishingActivity()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 5c0d424f4f42..2bebcc30c872 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1081,7 +1081,8 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayRotation dr = dc.getDisplayRotation(); spyOn(dr); doReturn(false).when(dr).useDefaultSettingsProvider(); - final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true) + .setComponent(getUniqueComponentName(mContext.getPackageName())).build(); app.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, app); assertFalse(dc.getRotationReversionController().isAnyOverrideActive()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index 35c9e3fb3aaf..f4fa12ea361d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -51,6 +51,7 @@ import static org.mockito.Mockito.times; import android.app.servertransaction.RefreshCallbackItem; import android.app.servertransaction.ResumeActivityItem; import android.content.ComponentName; +import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -592,6 +593,11 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { .setTask(mTask) .build(); + spyOn(mActivity.info.applicationInfo); + // Disable for camera compat. + doReturn(false).when(mActivity.info.applicationInfo).isChangeEnabled( + ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT); + spyOn(mActivity.mAtmService.getLifecycleManager()); spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index cf1dcd0515d1..7e8bd38fb6a9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -285,6 +285,52 @@ public class RootWindowContainerTests extends WindowTestsBase { } @Test + public void testTaskLayerRankFreeform() { + mSetFlagsRule.enableFlags(com.android.window.flags.Flags + .FLAG_PROCESS_PRIORITY_POLICY_FOR_MULTI_WINDOW_MODE); + final Task[] freeformTasks = new Task[3]; + final WindowProcessController[] processes = new WindowProcessController[3]; + for (int i = 0; i < freeformTasks.length; i++) { + freeformTasks[i] = new TaskBuilder(mSupervisor) + .setWindowingMode(WINDOWING_MODE_FREEFORM).setCreateActivity(true).build(); + final ActivityRecord r = freeformTasks[i].getTopMostActivity(); + r.setState(RESUMED, "test"); + processes[i] = r.app; + } + resizeDisplay(mDisplayContent, 2400, 2000); + // --------- + // | 2 | 1 | + // --------- + // | 0 | | + // --------- + freeformTasks[2].setBounds(0, 0, 1000, 1000); + freeformTasks[1].setBounds(1000, 0, 2000, 1000); + freeformTasks[0].setBounds(0, 1000, 1000, 2000); + mRootWindowContainer.rankTaskLayers(); + assertEquals(1, freeformTasks[2].mLayerRank); + assertEquals(2, freeformTasks[1].mLayerRank); + assertEquals(3, freeformTasks[0].mLayerRank); + assertFalse("Top doesn't need perceptible hint", (processes[2].getActivityStateFlags() + & WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM) != 0); + assertTrue((processes[1].getActivityStateFlags() + & WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM) != 0); + assertTrue((processes[0].getActivityStateFlags() + & WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM) != 0); + + // Make task2 occlude half of task0. + clearInvocations(mAtm); + freeformTasks[2].setBounds(0, 0, 1000, 1500); + waitHandlerIdle(mWm.mH); + // The process of task0 will demote from perceptible to visible. + final int stateFlags0 = processes[0].getActivityStateFlags(); + assertTrue((stateFlags0 + & WindowProcessController.ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE) != 0); + assertFalse((stateFlags0 + & WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM) != 0); + verify(mAtm).updateOomAdj(); + } + + @Test public void testForceStopPackage() { final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = task.getTopMostActivity(); 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 c30f70ee2903..e66dfeb8367c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -63,9 +63,9 @@ import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityRecord.State.STOPPED; +import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_POSITION_MULTIPLIER_CENTER; import static com.android.server.wm.AppCompatUtils.computeAspectRatio; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; -import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_POSITION_MULTIPLIER_CENTER; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.google.common.truth.Truth.assertThat; @@ -95,13 +95,11 @@ import android.compat.testing.PlatformCompatChangeRule; import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.ScreenOrientation; -import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.Rect; import android.os.Binder; -import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; @@ -1570,7 +1568,7 @@ public class SizeCompatTests extends WindowTestsBase { new TestSplitOrganizer(mAtm, activity.getDisplayContent()); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, 1000, 1400); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); @@ -1957,7 +1955,7 @@ public class SizeCompatTests extends WindowTestsBase { final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); // Move activity to multi-window which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight)); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -1993,7 +1991,7 @@ public class SizeCompatTests extends WindowTestsBase { final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); // Move activity to multi-window which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight)); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -2126,7 +2124,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(screenWidth, screenHeight); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mActivity.mWmService.mAppCompatConfiguration - .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true); + .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true); mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f); @@ -2147,7 +2145,7 @@ public class SizeCompatTests extends WindowTestsBase { final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(screenWidth), screenHeight); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -2392,13 +2390,11 @@ public class SizeCompatTests extends WindowTestsBase { spyOn(activity.mWmService.mAppCompatConfiguration); doReturn(enabled).when(activity.mWmService.mAppCompatConfiguration) .isUserAppAspectRatioSettingsEnabled(); - // Set user aspect ratio override - final IPackageManager pm = mAtm.getPackageManager(); - try { - doReturn(aspectRatio).when(pm) - .getUserMinAspectRatio(activity.packageName, activity.mUserId); - } catch (RemoteException ignored) { - } + final AppCompatAspectRatioOverrides aspectRatioOverrides = + activity.mAppCompatController.getAppCompatAspectRatioOverrides(); + spyOn(aspectRatioOverrides); + // Set user aspect ratio override. + doReturn(aspectRatio).when(aspectRatioOverrides).getUserMinAspectRatioOverrideCode(); prepareLimitedBounds(activity, screenOrientation, isUnresizable); @@ -2507,7 +2503,7 @@ public class SizeCompatTests extends WindowTestsBase { final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, activity.getDisplayContent()); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(screenWidth), screenHeight); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); @@ -2542,7 +2538,7 @@ public class SizeCompatTests extends WindowTestsBase { final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, activity.getDisplayContent()); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight)); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); @@ -2663,7 +2659,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(screenWidth, screenHeight); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mActivity.mWmService.mAppCompatConfiguration - .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true); + .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true); mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f); @@ -2684,7 +2680,7 @@ public class SizeCompatTests extends WindowTestsBase { final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight)); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -2712,7 +2708,7 @@ public class SizeCompatTests extends WindowTestsBase { final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, displayWidth, getExpectedSplitSize(displayHeight)); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -2748,7 +2744,7 @@ public class SizeCompatTests extends WindowTestsBase { final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(displayWidth), displayHeight); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -2879,7 +2875,7 @@ public class SizeCompatTests extends WindowTestsBase { // App bounds should be 700x1400 with the ratio as the display. assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height()); assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height() - / rotatedDisplayBounds.width(), rotatedActivityBounds.width()); + / rotatedDisplayBounds.width(), rotatedActivityBounds.width()); } @Test @@ -3328,7 +3324,7 @@ public class SizeCompatTests extends WindowTestsBase { final Rect originalBounds = new Rect(mActivity.getBounds()); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, 1000, 1400); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -3444,7 +3440,7 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_PORTRAIT); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, 1400, 1000); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -3468,7 +3464,7 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_LANDSCAPE); // Move activity to split screen which takes half of the screen. - mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false, "test"); organizer.mPrimary.setBounds(0, 0, 1000, 1400); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); @@ -3909,7 +3905,7 @@ public class SizeCompatTests extends WindowTestsBase { final DisplayPolicy policy = display.getDisplayPolicy(); DisplayPolicy.DecorInsets.Info decorInfo = policy.getDecorInsetsInfo(ROTATION_90, display.mBaseDisplayHeight, display.mBaseDisplayWidth); - decorInfo.mNonDecorInsets.set(130, 0, 60, 0); + decorInfo.mNonDecorInsets.set(130, 0, 60, 0); spyOn(policy); doReturn(decorInfo).when(policy).getDecorInsetsInfo(ROTATION_90, display.mBaseDisplayHeight, display.mBaseDisplayWidth); @@ -4145,6 +4141,7 @@ public class SizeCompatTests extends WindowTestsBase { // can be aligned inside parentAppBounds assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000)); } + @Test @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testApplyAspectRatio_activityCannotAlignWithParentAppHorizontal() { @@ -4198,7 +4195,7 @@ public class SizeCompatTests extends WindowTestsBase { final DisplayPolicy policy = display.getDisplayPolicy(); DisplayPolicy.DecorInsets.Info decorInfo = policy.getDecorInsetsInfo(ROTATION_90, display.mBaseDisplayHeight, display.mBaseDisplayWidth); - decorInfo.mNonDecorInsets.set(0, 130, 0, 60); + decorInfo.mNonDecorInsets.set(0, 130, 0, 60); spyOn(policy); doReturn(decorInfo).when(policy).getDecorInsetsInfo(ROTATION_90, display.mBaseDisplayHeight, display.mBaseDisplayWidth); @@ -4451,7 +4448,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true; // Set task as freeform mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); - prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds()); Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds()); @@ -4827,12 +4824,7 @@ public class SizeCompatTests extends WindowTestsBase { assertFalse(mActivity.isUniversalResizeable()); mDisplayContent.setIgnoreOrientationRequest(true); - final int swDp = mDisplayContent.getConfiguration().smallestScreenWidthDp; - if (swDp < WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP) { - final int height = 100 + (int) (mDisplayContent.getDisplayMetrics().density - * WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP); - resizeDisplay(mDisplayContent, 100 + height, height); - } + makeDisplayLargeScreen(mDisplayContent); assertTrue(mActivity.isUniversalResizeable()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index a215c0a80b46..757c358f51d0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1123,6 +1123,15 @@ public class WindowTestsBase extends SystemServiceTestsBase { displayContent.onRequestedOverrideConfigurationChanged(c); } + static void makeDisplayLargeScreen(DisplayContent displayContent) { + final int swDp = displayContent.getConfiguration().smallestScreenWidthDp; + if (swDp < WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP) { + final int height = 100 + (int) (displayContent.getDisplayMetrics().density + * WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP); + resizeDisplay(displayContent, 100 + height, height); + } + } + /** Used for the tests that assume the display is portrait by default. */ static void makeDisplayPortrait(DisplayContent displayContent) { if (displayContent.mBaseDisplayHeight <= displayContent.mBaseDisplayWidth) { @@ -1223,7 +1232,14 @@ public class WindowTestsBase extends SystemServiceTestsBase { } static ComponentName getUniqueComponentName() { - return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, + return getUniqueComponentName(DEFAULT_COMPONENT_PACKAGE_NAME); + } + + static ComponentName getUniqueComponentName(String packageName) { + if (packageName == null) { + packageName = DEFAULT_COMPONENT_PACKAGE_NAME; + } + return ComponentName.createRelative(packageName, DEFAULT_COMPONENT_CLASS_NAME + sCurrentActivityId++); } @@ -1298,8 +1314,7 @@ public class WindowTestsBase extends SystemServiceTestsBase { ActivityBuilder setActivityTheme(int theme) { mActivityTheme = theme; // Use the real package of test so it can get a valid context for theme. - mComponent = ComponentName.createRelative(mService.mContext.getPackageName(), - DEFAULT_COMPONENT_CLASS_NAME + sCurrentActivityId++); + mComponent = getUniqueComponentName(mService.mContext.getPackageName()); return this; } @@ -1743,7 +1758,7 @@ public class WindowTestsBase extends SystemServiceTestsBase { if (mIntent == null) { mIntent = new Intent(); if (mComponent == null) { - mComponent = getUniqueComponentName(); + mComponent = getUniqueComponentName(mPackage); } mIntent.setComponent(mComponent); mIntent.setFlags(mFlags); diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 44de65a009ff..79b3a7c4de65 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -582,6 +582,22 @@ public final class SatelliteManager { "android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED"; /** + * Meta-data represents whether the application supports P2P SMS over carrier roaming satellite + * which needs manual trigger to connect to satellite. The messaging applications that supports + * P2P SMS over carrier roaming satellites should add the following in their AndroidManifest. + * {@code + * <application + * <meta-data + * android:name="android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT" + * android:value="true"/> + * </application> + * } + * @hide + */ + public static final String METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT = + "android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT"; + + /** * Request to enable or disable the satellite modem and demo mode. * If satellite modem and cellular modem cannot work concurrently, * then this will disable the cellular modem if satellite modem is enabled, diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt index c6855b4a97b4..4ac567cc3937 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt @@ -285,7 +285,11 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : val displayRect = getDisplayRect(wmHelper) - val endX = if (isLeft) displayRect.left else displayRect.right + val endX = if (isLeft) { + displayRect.left + SNAP_RESIZE_DRAG_INSET + } else { + displayRect.right - SNAP_RESIZE_DRAG_INSET + } val endY = displayRect.centerY() / 2 // drag the window to snap resize @@ -391,6 +395,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : private companion object { val TIMEOUT: Duration = Duration.ofSeconds(3) + const val SNAP_RESIZE_DRAG_INSET: Int = 5 // inset to avoid dragging to display edge const val CAPTION: String = "desktop_mode_caption" const val MAXIMIZE_BUTTON_VIEW: String = "maximize_button_view" const val MAXIMIZE_MENU: String = "maximize_menu" diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 6742cbe1f19a..168141bf6e7d 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -41,6 +41,7 @@ android_test { "hamcrest-library", "junit-params", "kotlin-test", + "mockito-kotlin-nodeps", "mockito-target-extended-minus-junit4", "platform-test-annotations", "platform-screenshot-diff-core", diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 7526737f60bf..787ae06cd856 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -799,6 +799,27 @@ class KeyGestureControllerTests { } @Test + @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT) + fun testMoveToNextDisplay() { + val keyGestureController = KeyGestureController(context, testLooper.looper) + testKeyGestureInternal( + keyGestureController, + TestData( + "META + CTRL + D -> Move a task to next display", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_CTRL_LEFT, + KeyEvent.KEYCODE_D + ), + KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, + intArrayOf(KeyEvent.KEYCODE_D), + KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ) + ) + } + + @Test fun testCapsLockPressNotified() { val keyGestureController = KeyGestureController(context, testLooper.looper) val listener = KeyGestureEventListener() diff --git a/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt new file mode 100644 index 000000000000..47e7ac720a08 --- /dev/null +++ b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt @@ -0,0 +1,135 @@ +/* + * 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.server.input + +import android.content.Context +import android.content.ContextWrapper +import android.os.Handler +import android.os.test.TestLooper +import android.platform.test.annotations.Presubmit +import android.view.Display +import android.view.PointerIcon +import androidx.test.platform.app.InstrumentationRegistry +import junit.framework.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +/** + * Tests for {@link PointerIconCache}. + */ +@Presubmit +class PointerIconCacheTest { + + @get:Rule + val rule = MockitoJUnit.rule()!! + + @Mock + private lateinit var native: NativeInputManagerService + @Mock + private lateinit var defaultDisplay: Display + + private lateinit var context: Context + private lateinit var testLooper: TestLooper + private lateinit var cache: PointerIconCache + + @Before + fun setup() { + whenever(defaultDisplay.displayId).thenReturn(Display.DEFAULT_DISPLAY) + + context = object : ContextWrapper(InstrumentationRegistry.getInstrumentation().context) { + override fun getDisplay() = defaultDisplay + } + + testLooper = TestLooper() + cache = PointerIconCache(context, native, Handler(testLooper.looper)) + } + + @Test + fun testSetPointerScale() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setPointerScale(2f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 2, bitmap.height) + assertEquals(defaultBitmap.width * 2, bitmap.width) + } + + @Test + fun testSetAccessibilityScaleFactor() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setAccessibilityScaleFactor(Display.DEFAULT_DISPLAY, 4f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 4, bitmap.height) + assertEquals(defaultBitmap.width * 4, bitmap.width) + } + + @Test + fun testSetAccessibilityScaleFactorOnSecondaryDisplay() { + val defaultBitmap = getDefaultIcon().bitmap + val secondaryDisplayId = Display.DEFAULT_DISPLAY + 1 + cache.setAccessibilityScaleFactor(secondaryDisplayId, 4f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + assertEquals(defaultBitmap.height, bitmap.height) + assertEquals(defaultBitmap.width, bitmap.width) + + val bitmapSecondary = + cache.getLoadedPointerIcon(secondaryDisplayId, PointerIcon.TYPE_ARROW).bitmap + assertEquals(defaultBitmap.height * 4, bitmapSecondary.height) + assertEquals(defaultBitmap.width * 4, bitmapSecondary.width) + } + + @Test + fun testSetPointerScaleAndAccessibilityScaleFactor() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setPointerScale(2f) + cache.setAccessibilityScaleFactor(Display.DEFAULT_DISPLAY, 3f) + + testLooper.dispatchAll() + verify(native, times(2)).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 6, bitmap.height) + assertEquals(defaultBitmap.width * 6, bitmap.width) + } + + private fun getDefaultIcon() = + PointerIcon.getLoadedSystemIcon(context, PointerIcon.TYPE_ARROW, false, 1f) +} diff --git a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java index 6f3deab1d4fa..2692e12c57ed 100644 --- a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java @@ -40,7 +40,6 @@ import android.tools.traces.io.ResultWriter; import android.tools.traces.monitors.PerfettoTraceMonitor; import android.tools.traces.protolog.ProtoLogTrace; import android.tracing.perfetto.DataSource; -import android.util.proto.ProtoInputStream; import androidx.test.platform.app.InstrumentationRegistry; @@ -74,7 +73,7 @@ import java.util.concurrent.atomic.AtomicInteger; @SuppressWarnings("ConstantConditions") @Presubmit @RunWith(JUnit4.class) -public class PerfettoProtoLogImplTest { +public class ProcessedPerfettoProtoLogImplTest { private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog"; private static final String MOCK_VIEWER_CONFIG_FILE = "my/mock/viewer/config/file.pb"; private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation() @@ -100,7 +99,7 @@ public class PerfettoProtoLogImplTest { private static ProtoLogViewerConfigReader sReader; - public PerfettoProtoLogImplTest() throws IOException { + public ProcessedPerfettoProtoLogImplTest() throws IOException { } @BeforeClass @@ -151,7 +150,8 @@ public class PerfettoProtoLogImplTest { ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock( ViewerConfigInputStreamProvider.class); Mockito.when(viewerConfigInputStreamProvider.getInputStream()) - .thenAnswer(it -> new ProtoInputStream(sViewerConfigBuilder.build().toByteArray())); + .thenAnswer(it -> new AutoClosableProtoInputStream( + sViewerConfigBuilder.build().toByteArray())); sCacheUpdater = () -> {}; sReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider)); @@ -165,21 +165,16 @@ public class PerfettoProtoLogImplTest { throw new RuntimeException( "Unexpected viewer config file path provided"); } - return new ProtoInputStream(sViewerConfigBuilder.build().toByteArray()); + return new AutoClosableProtoInputStream(sViewerConfigBuilder.build().toByteArray()); }); }; sProtoLogConfigurationService = new ProtoLogConfigurationServiceImpl(dataSourceBuilder, tracer); - if (android.tracing.Flags.clientSideProtoLogging()) { - sProtoLog = new PerfettoProtoLogImpl( - MOCK_VIEWER_CONFIG_FILE, sReader, () -> sCacheUpdater.run(), - TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService); - } else { - sProtoLog = new PerfettoProtoLogImpl( - viewerConfigInputStreamProvider, sReader, () -> sCacheUpdater.run(), - TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService); - } + sProtoLog = new ProcessedPerfettoProtoLogImpl( + MOCK_VIEWER_CONFIG_FILE, viewerConfigInputStreamProvider, sReader, + () -> sCacheUpdater.run(), TestProtoLogGroup.values(), dataSourceBuilder, + sProtoLogConfigurationService); busyWaitForDataSourceRegistration(TEST_PROTOLOG_DATASOURCE_NAME); } @@ -398,18 +393,17 @@ public class PerfettoProtoLogImplTest { } @Test - public void log_logcatEnabledNoMessage() { + public void log_logcatEnabledNoMessageThrows() { when(sReader.getViewerString(anyLong())).thenReturn(null); PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - implSpy.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{5}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)")); - verify(sReader).getViewerString(eq(1234L)); + var assertion = assertThrows(RuntimeException.class, () -> + implSpy.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{5})); + Truth.assertThat(assertion).hasMessageThat() + .contains("Failed to decode message for logcat"); } @Test @@ -539,16 +533,12 @@ public class PerfettoProtoLogImplTest { PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) .build(); - long before; - long after; try { traceMonitor.start(); - before = SystemClock.elapsedRealtimeNanos(); sProtoLog.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash, 0b01100100, new Object[]{"test", 1, 0.1, true}); - after = SystemClock.elapsedRealtimeNanos(); } finally { traceMonitor.stop(mWriter); } @@ -606,7 +596,8 @@ public class PerfettoProtoLogImplTest { Truth.assertThat(stacktrace).doesNotContain(DataSource.class.getSimpleName() + ".java"); Truth.assertThat(stacktrace) .doesNotContain(ProtoLogImpl.class.getSimpleName() + ".java"); - Truth.assertThat(stacktrace).contains(PerfettoProtoLogImplTest.class.getSimpleName()); + Truth.assertThat(stacktrace) + .contains(ProcessedPerfettoProtoLogImplTest.class.getSimpleName()); Truth.assertThat(stacktrace).contains("stackTraceTrimmed"); } diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java index 8ecddaa76216..3d1e208189b0 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java @@ -47,12 +47,12 @@ public class ProtoLogTest { } @Test - public void throwOnRegisteringDuplicateGroup() { - final var assertion = assertThrows(RuntimeException.class, - () -> ProtoLog.init(TEST_GROUP_1, TEST_GROUP_1, TEST_GROUP_2)); + public void deduplicatesRegisteringDuplicateGroup() { + ProtoLog.init(TEST_GROUP_1, TEST_GROUP_1, TEST_GROUP_2); - Truth.assertThat(assertion).hasMessageThat().contains("" + TEST_GROUP_1.getId()); - Truth.assertThat(assertion).hasMessageThat().contains("duplicate"); + final var instance = ProtoLog.getSingleInstance(); + Truth.assertThat(instance.getRegisteredGroups()) + .containsExactly(TEST_GROUP_1, TEST_GROUP_2); } @Test diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java index d78ced161eaf..9e029a8d5e57 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java @@ -19,9 +19,12 @@ package com.android.internal.protolog; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import android.os.Build; import android.platform.test.annotations.Presubmit; -import android.util.proto.ProtoInputStream; +import com.google.common.truth.Truth; + +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,6 +32,8 @@ import org.junit.runners.JUnit4; import perfetto.protos.ProtologCommon; +import java.io.File; + @Presubmit @RunWith(JUnit4.class) public class ProtoLogViewerConfigReaderTest { @@ -83,7 +88,7 @@ public class ProtoLogViewerConfigReaderTest { ).build().toByteArray(); private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider = - () -> new ProtoInputStream(TEST_VIEWER_CONFIG); + () -> new AutoClosableProtoInputStream(TEST_VIEWER_CONFIG); private ProtoLogViewerConfigReader mConfig; @@ -123,6 +128,31 @@ public class ProtoLogViewerConfigReaderTest { } @Test + public void viewerConfigIsOnDevice() { + Assume.assumeFalse(Build.FINGERPRINT.contains("robolectric")); + + final String[] viewerConfigPaths; + if (android.tracing.Flags.perfettoProtologTracing()) { + viewerConfigPaths = new String[] { + "/system_ext/etc/wmshell.protolog.pb", + "/system/etc/core.protolog.pb", + }; + } else { + viewerConfigPaths = new String[] { + "/system_ext/etc/wmshell.protolog.json.gz", + "/system/etc/protolog.conf.json.gz", + }; + } + + for (final var viewerConfigPath : viewerConfigPaths) { + File f = new File(viewerConfigPath); + + Truth.assertWithMessage(f.getAbsolutePath() + " exists").that(f.exists()).isTrue(); + } + + } + + @Test public void loadUnloadAndReloadViewerConfig() { loadViewerConfig(); unloadViewerConfig(); diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt index caa018d8c013..e163ef470355 100644 --- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt @@ -485,6 +485,7 @@ val exemptAidlInterfaces = setOf( "android.net.thread.IConfigurationReceiver", "android.net.thread.IOperationalDatasetCallback", "android.net.thread.IOperationReceiver", + "android.net.thread.IOutputReceiver", "android.net.thread.IStateCallback", "android.net.thread.IThreadNetworkController", "android.net.thread.IThreadNetworkManager", @@ -757,6 +758,7 @@ val exemptAidlInterfaces = setOf( "com.android.server.thread.openthread.IChannelMasksReceiver", "com.android.server.thread.openthread.INsdPublisher", "com.android.server.thread.openthread.IOtDaemonCallback", + "com.android.server.thread.openthread.IOtOutputReceiver", "com.android.server.thread.openthread.IOtStatusReceiver", "com.google.android.clockwork.ambient.offload.IDisplayOffloadService", "com.google.android.clockwork.ambient.offload.IDisplayOffloadTransitionFinishedCallbacks", |