diff options
299 files changed, 9336 insertions, 1778 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 12f455ad0144..bd5c00600044 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -2258,7 +2258,8 @@ public class AppStandbyController } synchronized (mSystemExemptionAppOpMode) { if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - mSystemExemptionAppOpMode.delete(UserHandle.getUid(userId, getAppId(pkgName))); + final int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID); + mSystemExemptionAppOpMode.delete(uid); } } diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt index 43caaecebdaf..caa1929abd54 100644 --- a/api/coverage/tools/ExtractFlaggedApis.kt +++ b/api/coverage/tools/ExtractFlaggedApis.kt @@ -43,7 +43,7 @@ fun main(args: Array<String>) { .setClassName(it.fullName()) .setMethodName(method.name()) for (param in method.parameters()) { - api.addParameterTypes(param.type().toTypeString()) + api.addParameters(param.type().toTypeString()) } if (builder.containsFlagToApi(flagValue)) { var updatedApis = diff --git a/api/coverage/tools/extract_flagged_apis.proto b/api/coverage/tools/extract_flagged_apis.proto index 031d621b178f..4db2a8b03de5 100644 --- a/api/coverage/tools/extract_flagged_apis.proto +++ b/api/coverage/tools/extract_flagged_apis.proto @@ -32,6 +32,6 @@ message JavaMethod { string package_name = 1; string class_name = 2; string method_name = 3; - repeated string parameter_types = 4; + repeated string parameters = 4; } diff --git a/config/dirty-image-objects b/config/dirty-image-objects index 2584610e2848..f2e2b82cd82a 100644 --- a/config/dirty-image-objects +++ b/config/dirty-image-objects @@ -16,282 +16,1713 @@ # # # Dirty-image-objects file for boot image. -# -# Objects in this file are known dirty at runtime. Current this includes: -# - classes with known dirty static fields. -# # 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 file can be generated using imgdiag with a command such as: -# adb shell imgdiag --image-diff-pid=<app pid> --zygote-diff-pid=<zygote pid> \ -# --boot-image=/system/framework/boot.art --dump-dirty-objects -# Then, grep for lines containing "Private dirty object" from the output. -# This particular file was generated by dumping systemserver and systemui. -# -Landroid/animation/LayoutTransition; -Landroid/app/ActivityManager; -Landroid/app/ActivityTaskManager; -Landroid/app/ActivityThread; -Landroid/app/AlarmManager; -Landroid/app/AppOpsManager; -Landroid/app/ContextImpl; -Landroid/app/Notification; -Landroid/app/NotificationManager; -Landroid/app/PendingIntent$FinishedDispatcher; -Landroid/app/PropertyInvalidatedCache$NoPreloadHolder; -Landroid/app/QueuedWork; -Landroid/app/ResourcesManager; -Landroid/app/SystemServiceRegistry; -Landroid/app/WallpaperManager; -Landroid/app/backup/BackupManager; -Landroid/compat/Compatibility; -Landroid/content/AsyncQueryHandler; -Landroid/content/ContentProviderClient; -Landroid/content/ContentResolver; -Landroid/content/Context; -Landroid/content/pm/PackageItemInfo; -Landroid/content/pm/UserPackage; -Landroid/content/res/ResourceTimer; -Landroid/database/CursorWindow; -Landroid/database/sqlite/SQLiteCompatibilityWalFlags; -Landroid/database/sqlite/SQLiteDebug$NoPreloadHolder; -Landroid/database/sqlite/SQLiteGlobal; -Landroid/ddm/DdmHandleAppName; -Landroid/graphics/Bitmap; -Landroid/graphics/Canvas; -Landroid/graphics/Compatibility; -Landroid/graphics/HardwareRenderer; -Landroid/graphics/TemporaryBuffer; -Landroid/graphics/Typeface; -Landroid/graphics/drawable/AdaptiveIconDrawable; -Landroid/hardware/SensorPrivacyManager; -Landroid/hardware/SystemSensorManager; -Landroid/hardware/devicestate/DeviceStateManagerGlobal; -Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal; -Landroid/hardware/display/DisplayManagerGlobal; -Landroid/hardware/input/InputManagerGlobal; -Landroid/hardware/location/GeofenceHardwareImpl; -Landroid/icu/impl/number/range/StandardPluralRanges; -Landroid/icu/text/Collator; -Landroid/icu/util/TimeZone; -Landroid/location/LocationManager; -Landroid/media/AudioManager; -Landroid/media/AudioPlaybackConfiguration; -Landroid/media/AudioSystem; -Landroid/media/MediaCodec; -Landroid/media/MediaCodecList; -Landroid/media/MediaFrameworkPlatformInitializer; -Landroid/media/MediaRouter2Manager; -Landroid/media/MediaRouter; -Landroid/media/PlayerBase; -Landroid/media/audiopolicy/AudioProductStrategy; -Landroid/media/audiopolicy/AudioVolumeGroup; -Landroid/nfc/NfcAdapter; -Landroid/nfc/NfcFrameworkInitializer; -Landroid/nfc/cardemulation/CardEmulation; -Landroid/os/AsyncTask; -Landroid/os/BaseBundle; -Landroid/os/Binder; -Landroid/os/BinderProxy; -Landroid/os/Environment; -Landroid/os/FileObserver; -Landroid/os/Handler; -Landroid/os/LocaleList; -Landroid/os/Looper; -Landroid/os/Message; -Landroid/os/NullVibrator; -Landroid/os/Parcel; -Landroid/os/Process; -Landroid/os/ServiceManager; -Landroid/os/StrictMode; -Landroid/os/UEventObserver; -Landroid/os/UserManager; -Landroid/os/WorkSource; -Landroid/os/storage/StorageManager; -Landroid/permission/PermissionManager; -Landroid/provider/DeviceConfigInitializer; -Landroid/provider/FontsContract; -Landroid/provider/Settings; -Landroid/renderscript/RenderScript; -Landroid/renderscript/RenderScriptCacheDir; -Landroid/security/keystore2/KeyStoreCryptoOperationUtils; -Landroid/security/net/config/ApplicationConfig; -Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder; -Landroid/security/net/config/UserCertificateSource$NoPreloadHolder; -Landroid/telecom/Log; -Landroid/telecom/TelecomManager; -Landroid/telephony/AnomalyReporter; -Landroid/telephony/TelephonyFrameworkInitializer; -Landroid/telephony/TelephonyLocalConnection; -Landroid/telephony/TelephonyManager; -Landroid/telephony/TelephonyRegistryManager; -Landroid/text/DynamicLayout; -Landroid/text/TextUtils; -Landroid/text/format/DateFormat; -Landroid/text/format/DateUtils; -Landroid/text/method/ArrowKeyMovementMethod; -Landroid/text/method/LinkMovementMethod; -Landroid/text/method/SingleLineTransformationMethod; -Landroid/text/style/ClickableSpan; -Landroid/timezone/TelephonyLookup; -Landroid/timezone/TimeZoneFinder; -Landroid/util/ArrayMap; -Landroid/util/ArraySet; -Landroid/util/EventLog; -Landroid/util/NtpTrustedTime; -Landroid/view/Choreographer; -Landroid/view/CrossWindowBlurListeners; -Landroid/view/DisplayCutout; -Landroid/view/KeyEvent; -Landroid/view/MotionEvent; -Landroid/view/PointerIcon; -Landroid/view/RoundedCorners; -Landroid/view/SurfaceControl; -Landroid/view/View; -Landroid/view/ViewGroup$TouchTarget; -Landroid/view/ViewRootImpl; -Landroid/view/ViewTreeObserver; -Landroid/view/WindowManagerGlobal; -Landroid/view/accessibility/AccessibilityManager; -Landroid/view/accessibility/AccessibilityNodeIdManager; -Landroid/view/autofill/Helper; -Landroid/view/inputmethod/IInputMethodManagerGlobalInvoker; -Landroid/view/inputmethod/InputMethodManager; -Landroid/webkit/CookieSyncManager; -Landroid/webkit/WebView; -Landroid/webkit/WebViewFactory; -Landroid/webkit/WebViewZygote; -Landroid/widget/AbsListView; -Landroid/widget/ImageView; -Landroid/widget/LinearLayout; -Landroid/widget/Toast; -Landroid/window/SurfaceSyncGroup; -Lcom/android/i18n/timezone/TelephonyLookup; -Lcom/android/i18n/timezone/TimeZoneFinder; -Lcom/android/internal/config/appcloning/AppCloningDeviceConfigHelper; -Lcom/android/internal/content/om/OverlayConfig; -Lcom/android/internal/display/BrightnessSynchronizer; -Lcom/android/internal/infra/AndroidFuture; -Lcom/android/internal/inputmethod/ImeTracing; -Lcom/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry; -Lcom/android/internal/jank/InteractionJankMonitor$InstanceHolder; -Lcom/android/internal/jank/InteractionJankMonitor; -Lcom/android/internal/logging/MetricsLogger; -Lcom/android/internal/os/BackgroundThread; -Lcom/android/internal/os/BinderInternal; -Lcom/android/internal/os/KernelCpuBpfTracking; -Lcom/android/internal/os/RuntimeInit; -Lcom/android/internal/os/SomeArgs; -Lcom/android/internal/os/ZygoteInit; -Lcom/android/internal/policy/AttributeCache; -Lcom/android/internal/protolog/BaseProtoLogImpl; -Lcom/android/internal/protolog/ProtoLogImpl; -Lcom/android/internal/statusbar/NotificationVisibility; -Lcom/android/internal/telephony/CellBroadcastServiceManager; -Lcom/android/internal/telephony/IntentBroadcaster; -Lcom/android/internal/telephony/MccTable; -Lcom/android/internal/telephony/MultiSimSettingController; -Lcom/android/internal/telephony/PackageChangeReceiver; -Lcom/android/internal/telephony/PhoneConfigurationManager; -Lcom/android/internal/telephony/PhoneFactory; -Lcom/android/internal/telephony/ProxyController; -Lcom/android/internal/telephony/RILRequest; -Lcom/android/internal/telephony/RadioConfig; -Lcom/android/internal/telephony/RadioInterfaceCapabilityController; -Lcom/android/internal/telephony/SmsApplication; -Lcom/android/internal/telephony/SmsBroadcastUndelivered; -Lcom/android/internal/telephony/SomeArgs; -Lcom/android/internal/telephony/TelephonyComponentFactory; -Lcom/android/internal/telephony/TelephonyDevController; -Lcom/android/internal/telephony/cat/CatService; -Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler; -Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager; -Lcom/android/internal/telephony/euicc/EuiccCardController; -Lcom/android/internal/telephony/euicc/EuiccController; -Lcom/android/internal/telephony/ims/ImsResolver; -Lcom/android/internal/telephony/metrics/TelephonyMetrics; -Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierIdMismatch; -Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularDataServiceSwitch; -Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularServiceState; -Lcom/android/internal/telephony/nano/PersistAtomsProto$DataCallSession; -Lcom/android/internal/telephony/nano/PersistAtomsProto$EmergencyNumbersInfo; -Lcom/android/internal/telephony/nano/PersistAtomsProto$GbaEvent; -Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerEvent; -Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerListenerEvent; -Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationFeatureTagStats; -Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationServiceDescStats; -Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationStats; -Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationTermination; -Lcom/android/internal/telephony/nano/PersistAtomsProto$IncomingSms; -Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequests; -Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequestsV2; -Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingShortCodeSms; -Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingSms; -Lcom/android/internal/telephony/nano/PersistAtomsProto$PresenceNotifyEvent; -Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsAcsProvisioningStats; -Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsClientProvisioningStats; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteController; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteIncomingDatagram; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteOutgoingDatagram; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteProvision; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSession; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSosMessageRecommender; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SipDelegateStats; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SipMessageResponse; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportFeatureTagStats; -Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportSession; -Lcom/android/internal/telephony/nano/PersistAtomsProto$UceEventStats; -Lcom/android/internal/telephony/nano/PersistAtomsProto$UnmeteredNetworks; -Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallRatUsage; -Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallSession; -Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall; -Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall; -Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyServiceState$NetworkRegistrationInfo; -Lcom/android/internal/telephony/satellite/PointingAppController; -Lcom/android/internal/telephony/satellite/SatelliteModemInterface; -Lcom/android/internal/telephony/uicc/UiccController; -Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher; -Lcom/android/internal/util/ContrastColorUtil; -Lcom/android/internal/view/WindowManagerPolicyThread; -Lcom/android/org/bouncycastle/crypto/CryptoServicesRegistrar; -Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo; -Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo; -Lcom/android/server/AppWidgetBackupBridge; -Ldalvik/system/BaseDexClassLoader; -Ldalvik/system/BlockGuard; -Ldalvik/system/CloseGuard; -Ldalvik/system/RuntimeHooks; -Ldalvik/system/SocketTagger; -Ldalvik/system/VMRuntime; -Ldalvik/system/ZipPathValidator; -Ldalvik/system/ZygoteHooks; -Ljava/lang/System; -Ljava/lang/Thread; -Ljava/lang/Throwable; -Ljava/lang/ref/FinalizerReference; -Ljava/lang/ref/ReferenceQueue; -Ljava/net/ResponseCache; -Ljava/nio/Bits; -Ljava/nio/charset/Charset; -Ljava/security/Provider; -Ljava/util/Collections; -Ljava/util/GregorianCalendar; -Ljava/util/Locale$NoImagePreloadHolder; -Ljava/util/Locale; -Ljava/util/Scanner; -Ljava/util/TimeZone; -Ljava/util/concurrent/ForkJoinPool; -Ljava/util/concurrent/ThreadLocalRandom; -Ljavax/net/ServerSocketFactory; -Ljavax/net/SocketFactory; -Ljavax/net/ssl/HttpsURLConnection$NoPreloadHolder; -Ljavax/net/ssl/HttpsURLConnection; -Ljavax/net/ssl/SSLContext; -Ljavax/net/ssl/SSLServerSocketFactory; -Ljavax/net/ssl/SSLSocketFactory; -Llibcore/io/Libcore; -Llibcore/net/NetworkSecurityPolicy; -Lsun/misc/Cleaner; -Lsun/nio/ch/FileChannelImpl$Unmapper; -Lsun/nio/ch/FileChannelImpl; -Lsun/security/jca/Providers; +Landroid/text/style/URLSpan; 0 +Landroid/content/res/Resources$NotFoundException; 1 +Landroid/os/PowerManager$WakeLock; 2 +Landroid/os/BatterySaverPolicyConfig; 2 +Landroid/content/ContextWrapper; 2 +Landroid/app/WallpaperInfo; 2 +Landroid/content/pm/PackageManager; 2 +Landroid/app/IWallpaperManager; 2 +Ljava/lang/BootClassLoader; 2 +Ljava/time/Duration; 2 +Landroid/util/Printer; 2 +Landroid/app/WallpaperManager$OnColorsChangedListener; 2 +Landroid/app/WallpaperColors; 2 +Landroid/content/pm/ServiceInfo; 2 +Landroid/app/KeyguardManager$KeyguardDismissCallback; 2 +Ljava/lang/CharSequence; 3 +Landroid/widget/Switch; 4 +Lcom/android/internal/util/ContrastColorUtil; 4 +Landroid/view/SurfaceControl; 4 +Landroid/graphics/ColorMatrix;.dexCache:Ljava/lang/Object; 4 +Lcom/android/internal/widget/CachingIconView; 4 +Landroid/window/IRemoteTransition$Stub$Proxy; 4 +Landroid/app/trust/TrustManager$TrustListener; 4 +Landroid/view/NotificationHeaderView; 4 +Lcom/android/internal/widget/ImageResolver; 4 +Landroid/window/WindowContainerTransaction$Change; 4 +Lcom/android/internal/widget/MessagingLayout; 4 +Ljava/util/concurrent/ConcurrentLinkedQueue; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry; 4 +Landroid/view/RemotableViewMethod; 4 +Landroid/app/IApplicationThread$Stub$Proxy; 4 +Landroid/os/FileUtils; 4 +Landroid/view/View;.SCALE_X:Landroid/util/Property; 4 +Landroid/widget/GridLayout;.UNDEFINED_ALIGNMENT:Landroid/widget/GridLayout$Alignment; 4 +Landroid/media/MediaPlayer$EventHandler; 4 +Landroid/widget/DateTimeView; 4 +Llibcore/util/ZoneInfo; 4 +Lcom/android/internal/statusbar/IStatusBarService; 4 +Ljava/lang/invoke/MethodType;.internTable:Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet;.stale:Ljava/lang/ref/ReferenceQueue; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry; 4 +Lcom/android/internal/logging/UiEventLogger; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry; 4 +Landroid/renderscript/RenderScript; 4 +Landroid/view/ViewTreeObserver$OnWindowVisibilityChangeListener; 4 +Lcom/android/internal/widget/RemeasuringLinearLayout; 4 +Landroid/widget/DateTimeView$ReceiverInfo$1; 4 +Landroid/view/View;.TRANSLATION_Y:Landroid/util/Property; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry; 4 +Lcom/android/internal/widget/NotificationExpandButton; 4 +Lcom/android/internal/view/menu/ActionMenuItemView; 4 +Landroid/view/animation/AnimationSet; 4 +Landroid/hardware/biometrics/BiometricSourceType;.FINGERPRINT:Landroid/hardware/biometrics/BiometricSourceType; 4 +Landroid/window/WindowOrganizer;.IWindowOrganizerControllerSingleton:Landroid/util/Singleton; 4 +Ljava/lang/Runnable; 4 +Lorg/apache/harmony/dalvik/ddmc/DdmServer;.mHandlerMap:Ljava/util/HashMap; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry; 4 +Lcom/android/internal/widget/ImageFloatingTextView; 4 +Landroid/window/IWindowContainerToken$Stub$Proxy; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry; 4 +Landroid/content/res/ColorStateList; 4 +Landroid/view/View;.SCALE_Y:Landroid/util/Property; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap; 4 +Lcom/android/internal/widget/ConversationLayout; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry;.right:Ljava/util/TreeMap$TreeMapEntry; 4 +Lcom/android/internal/colorextraction/ColorExtractor$OnColorsChangedListener; 4 +Landroid/hardware/face/FaceManager$FaceDetectionCallback; 4 +Landroid/widget/RemoteViews;.sLookupKey:Landroid/widget/RemoteViews$MethodKey; 4 +Landroid/widget/ViewSwitcher;.dexCache:Ljava/lang/Object; 4 +Lcom/android/internal/widget/NotificationActionListLayout; 4 +Ljava/util/concurrent/ConcurrentLinkedQueue$Node; 4 +Landroid/hardware/biometrics/BiometricSourceType;.FACE:Landroid/hardware/biometrics/BiometricSourceType; 4 +Landroid/hardware/biometrics/BiometricSourceType;.IRIS:Landroid/hardware/biometrics/BiometricSourceType; 4 +Landroid/view/NotificationTopLineView; 4 +Lcom/android/internal/protolog/BaseProtoLogImpl;.LOG_GROUPS:Ljava/util/TreeMap;.root:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry;.left:Ljava/util/TreeMap$TreeMapEntry; 4 +Landroid/widget/RemoteViews;.sMethods:Landroid/util/ArrayMap; 4 +Lcom/android/internal/os/BinderInternal$BinderProxyLimitListener; 5 +Landroid/app/AppOpsManager$OnOpNotedInternalListener; 5 +Lcom/android/internal/R$styleable;.WindowAnimation:[I 5 +Lcom/android/internal/logging/UiEventLogger$UiEventEnum; 5 +Lcom/android/internal/policy/AttributeCache; 5 +Landroid/app/Notification$CallStyle; 5 +Landroid/app/AppOpsManager$OnOpNotedListener; 5 +Lcom/android/internal/protolog/BaseProtoLogImpl; 5 +Landroid/app/AppOpsManager$OnOpStartedListener; 5 +Lcom/android/internal/util/ScreenshotHelper$1; 5 +Landroid/app/Notification$DecoratedCustomViewStyle; 5 +Landroid/view/DisplayCutout; 5 +Landroid/view/InputEvent;.mNextSeq:Ljava/util/concurrent/atomic/AtomicInteger; 5 +Lcom/android/internal/statusbar/NotificationVisibility; 5 +Landroid/telephony/DataSpecificRegistrationInfo; 6 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle; 7 +Landroid/telephony/VoiceSpecificRegistrationInfo; 8 +Landroid/telephony/AnomalyReporter; 8 +Landroid/telephony/TelephonyRegistryManager;.sCarrierPrivilegeCallbacks:Ljava/util/WeakHashMap; 8 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry; 8 +Landroid/app/LoadedApk$ServiceDispatcher$InnerConnection; 8 +Landroid/content/ContentProvider$Transport; 8 +Landroid/telephony/NetworkRegistrationInfo; 8 +Landroid/net/MatchAllNetworkSpecifier; 8 +Landroid/telephony/TelephonyRegistryManager;.sCarrierPrivilegeCallbacks:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry; 8 +Landroid/app/PropertyInvalidatedCache;.sInvalidates:Ljava/util/HashMap; 9 +Landroid/app/PropertyInvalidatedCache$NoPreloadHolder; 9 +Landroid/app/PropertyInvalidatedCache;.sDisabledKeys:Ljava/util/HashSet;.map:Ljava/util/HashMap; 10 +Landroid/media/AudioSystem$AudioRecordingCallback; 11 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedString; 11 +Landroid/net/metrics/IpManagerEvent; 11 +Lcom/android/internal/os/ProcessCpuTracker$FilterStats; 11 +Lcom/android/internal/infra/AbstractRemoteService$AsyncRequest; 11 +Landroid/content/pm/RegisteredServicesCache$3; 11 +Lcom/android/internal/os/LooperStats; 11 +Lcom/android/server/AppWidgetBackupBridge; 11 +Landroid/hardware/display/DisplayManagerInternal; 11 +Landroid/content/pm/PackageInfo; 11 +Landroid/hardware/soundtrigger/SoundTriggerModule$EventHandlerDelegate; 11 +Landroid/app/servertransaction/ResumeActivityItem; 11 +Lcom/android/internal/widget/AlertDialogLayout; 11 +Landroid/content/pm/FallbackCategoryProvider;.sFallbacks:Landroid/util/ArrayMap; 11 +Landroid/os/RemoteCallback$1; 11 +Landroid/content/pm/SharedLibraryInfo; 11 +Landroid/util/MemoryIntArray; 11 +Landroid/net/metrics/DhcpErrorEvent; 11 +Lcom/android/internal/util/function/DodecConsumer; 11 +Landroid/provider/Settings; 11 +Landroid/app/PropertyInvalidatedCache;.sCorkLock:Ljava/lang/Object; 11 +Lcom/android/internal/os/CachedDeviceState$Readonly; 11 +Landroid/app/job/JobServiceEngine$JobHandler; 11 +Landroid/app/SystemServiceRegistry; 11 +Lcom/android/internal/os/BinderInternal$CallStatsObserver; 11 +Lcom/android/internal/statusbar/IStatusBar$Stub$Proxy; 11 +Landroid/hardware/location/IActivityRecognitionHardwareClient; 11 +Landroid/telecom/Logging/EventManager$EventListener; 11 +Landroid/accounts/AccountManagerInternal; 11 +Lcom/android/internal/os/KernelCpuBpfTracking; 11 +Lcom/android/internal/statusbar/NotificationVisibility$NotificationLocation; 11 +Landroid/hardware/camera2/CameraManager$CameraManagerGlobal; 11 +Landroid/os/ServiceSpecificException; 11 +Landroid/net/Uri$PathPart;.NULL:Landroid/net/Uri$PathPart; 11 +Landroid/app/ActivityManagerInternal; 11 +Landroid/media/AudioSystem; 11 +Landroid/service/dreams/DreamManagerInternal; 11 +Landroid/debug/AdbManagerInternal; 11 +Landroid/graphics/Bitmap$CompressFormat; 11 +Landroid/hardware/location/NanoAppMessage; 11 +Landroid/os/storage/StorageManagerInternal; 11 +Landroid/app/AppOpsManagerInternal; 11 +Ljava/security/cert/CertificateException; 11 +Ldalvik/system/VMRuntime; 11 +Landroid/content/pm/SigningInfo; 11 +Landroid/view/KeyEvent; 11 +Lcom/android/internal/view/WindowManagerPolicyThread; 11 +Landroid/graphics/Region;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 11 +Landroid/content/res/ResourceTimer; 11 +Landroid/view/autofill/AutofillManagerInternal; 11 +Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object; 11 +Landroid/graphics/Region;.sPool:Landroid/util/Pools$SynchronizedPool; 11 +Landroid/app/LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0; 11 +Lcom/android/server/LocalServices;.sLocalServiceObjects:Landroid/util/ArrayMap; 11 +Landroid/app/admin/DevicePolicyManagerInternal$OnCrossProfileWidgetProvidersChangeListener; 11 +Landroid/accounts/AccountManagerInternal$OnAppPermissionChangeListener; 11 +Landroid/content/pm/PermissionInfo; 11 +Landroid/view/WindowManagerPolicyConstants$PointerEventListener; 11 +Landroid/os/UEventObserver; 11 +Landroid/media/AudioManagerInternal$RingerModeDelegate; 11 +Landroid/view/Display$HdrCapabilities; 11 +Landroid/service/notification/Condition; 11 +Landroid/content/pm/UserPackage; 11 +Landroid/app/AppOpsManager$SamplingStrategy; 11 +Landroid/telephony/ServiceState; 11 +Landroid/app/servertransaction/PauseActivityItem; 11 +Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sMessageCallbacksPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool;.mLock:Ljava/lang/Object; 11 +Landroid/view/KeyCharacterMap$FallbackAction; 11 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringArray; 11 +Landroid/hardware/display/DeviceProductInfo; 11 +Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap;.mHashes:[I 11 +Landroid/content/pm/RegisteredServicesCache$2; 11 +Landroid/content/pm/PackageManager;.sCacheAutoCorker:Landroid/app/PropertyInvalidatedCache$AutoCorker; 11 +Landroid/app/PropertyInvalidatedCache;.sCorks:Ljava/util/HashMap; 11 +Landroid/service/notification/StatusBarNotification; 11 +Landroid/app/servertransaction/ConfigurationChangeItem; 11 +Landroid/app/ActivityManager$RecentTaskInfo; 11 +Landroid/app/Notification; 11 +Landroid/app/servertransaction/DestroyActivityItem; 11 +Landroid/webkit/WebViewLibraryLoader$RelroFileCreator; 11 +Landroid/net/metrics/NetworkEvent; 11 +Landroid/media/AudioPlaybackConfiguration; 11 +Landroid/accessibilityservice/AccessibilityServiceInfo; 11 +Landroid/hardware/display/DeviceProductInfo$ManufactureDate; 11 +Landroid/os/storage/StorageVolume; 11 +Landroid/os/BatteryManagerInternal; 11 +Landroid/appwidget/AppWidgetManagerInternal; 11 +Landroid/app/servertransaction/NewIntentItem; 11 +Landroid/content/pm/ShortcutServiceInternal; 11 +Landroid/app/assist/ActivityId; 11 +Landroid/window/DisplayAreaAppearedInfo; 11 +Landroid/os/Process;.ZYGOTE_PROCESS:Landroid/os/ZygoteProcess;.mLock:Ljava/lang/Object; 11 +Landroid/app/usage/UsageStats; 11 +Landroid/app/Notification$MediaStyle; 11 +Landroid/media/AudioSystem$DynamicPolicyCallback; 11 +Landroid/content/pm/ProviderInfo; 11 +Landroid/os/PowerManagerInternal; 11 +Landroid/service/voice/VoiceInteractionManagerInternal; 11 +Landroid/content/pm/FeatureInfo; 11 +Landroid/app/servertransaction/TopResumedActivityChangeItem; 11 +Landroid/app/Notification$DecoratedMediaCustomViewStyle; 11 +Landroid/appwidget/AppWidgetProviderInfo; 11 +Landroid/app/AppOpsManager$NoteOpEvent; 11 +Landroid/graphics/GraphicsStatsService; 11 +Landroid/view/DisplayAddress$Physical; 11 +Landroid/content/ComponentName$WithComponentName; 11 +Landroid/app/admin/DevicePolicyManagerInternal; 11 +Landroid/os/ResultReceiver$MyResultReceiver; 11 +Landroid/content/ContentProviderClient; 11 +Landroid/content/pm/RegisteredServicesCache$1; 11 +Landroid/app/PendingIntent$FinishedDispatcher; 11 +Landroid/location/LocationManager; 11 +Landroid/hardware/location/ContextHubInfo; 11 +Landroid/content/pm/ShortcutServiceInternal$ShortcutChangeListener; 11 +Lcom/android/server/usage/AppStandbyInternal; 11 +Landroid/content/pm/RegisteredServicesCacheListener; 11 +Landroid/app/servertransaction/LaunchActivityItem; 11 +Landroid/content/pm/BaseParceledListSlice$1; 11 +Landroid/annotation/StringRes; 11 +Lcom/android/internal/R$styleable;.Window:[I 11 +Landroid/service/notification/ZenModeConfig; 11 +Landroid/telecom/Logging/SessionManager$ISessionListener; 11 +Landroid/app/time/TimeZoneConfiguration; 11 +Landroid/net/metrics/ValidationProbeEvent; 11 +Landroid/content/pm/PackageInstaller$SessionInfo; 11 +Landroid/content/pm/UserPackage;.sCache:Landroid/util/SparseArrayMap;.mData:Landroid/util/SparseArray; 11 +Landroid/content/pm/PermissionGroupInfo; 11 +Landroid/hardware/sidekick/SidekickInternal; 11 +Lcom/android/internal/widget/ButtonBarLayout; 11 +Landroid/content/pm/LauncherActivityInfoInternal; 11 +Lcom/android/internal/util/Parcelling$Cache;.sCache:Landroid/util/ArrayMap; 11 +Lcom/android/internal/widget/LockSettingsInternal; 11 +Landroid/media/AudioManagerInternal; 11 +Landroid/app/AppOpsManager$AttributedOpEntry; 11 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringList; 11 +Landroid/telecom/Log; 11 +Landroid/app/time/TimeZoneCapabilities; 11 +Landroid/attention/AttentionManagerInternal; 11 +Landroid/view/WindowManagerPolicyConstants; 11 +Landroid/content/pm/CrossProfileAppsInternal; 11 +Landroid/hardware/location/GeofenceHardwareService; 11 +Landroid/content/pm/dex/ArtManagerInternal; 11 +Landroid/net/metrics/IpReachabilityEvent; 11 +Landroid/content/pm/LauncherApps$ShortcutQuery$QueryFlags; 11 +Landroid/media/AudioAttributes; 11 +Landroid/app/PropertyInvalidatedCache$AutoCorker$1; 11 +Landroid/net/metrics/ApfProgramEvent; 11 +Landroid/content/pm/SigningDetails; 11 +Lcom/android/internal/protolog/ProtoLogImpl; 11 +Landroid/hardware/biometrics/ComponentInfoInternal; 11 +Lcom/android/internal/util/ToBooleanFunction; 11 +Landroid/app/ActivityThread$H; 11 +Landroid/hardware/location/GeofenceHardwareImpl; 11 +Landroid/net/wifi/nl80211/WifiNl80211Manager$ScanEventHandler; 11 +Landroid/util/NtpTrustedTime; 11 +Landroid/hardware/soundtrigger/SoundTrigger$StatusListener; 11 +Lcom/android/internal/app/procstats/AssociationState;.sTmpSourceKey:Lcom/android/internal/app/procstats/AssociationState$SourceKey; 11 +Ljava/util/zip/ZipFile$ZipFileInflaterInputStream; 11 +Landroid/app/job/JobInfo; 11 +Lcom/android/internal/content/om/OverlayConfig; 11 +Landroid/webkit/WebViewZygote; 11 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringSet; 11 +Lcom/android/internal/infra/AbstractRemoteService$VultureCallback; 11 +Landroid/permission/PermissionManagerInternal; 11 +Lcom/android/server/WidgetBackupProvider; 11 +Landroid/window/WindowOnBackInvokedDispatcher$OnBackInvokedCallbackWrapper; 11 +Landroid/app/PropertyInvalidatedCache;.sCorkedInvalidates:Ljava/util/HashMap; 11 +Landroid/media/AudioPlaybackConfiguration$PlayerDeathMonitor; 11 +Landroid/net/wifi/nl80211/WifiNl80211Manager$ScanEventCallback; 11 +Landroid/service/notification/NotificationListenerService$RankingMap; 11 +Landroid/os/UserHandle;.sExtraUserHandleCache:Landroid/util/SparseArray; 11 +Ljava/time/DateTimeException; 11 +Ljava/lang/NumberFormatException; 11 +Ljava/security/Provider;.knownEngines:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.125:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 11 +Landroid/app/LoadedApk$ServiceDispatcher$RunConnection; 11 +Landroid/view/RoundedCorners; 11 +Landroid/os/Process;.ZYGOTE_PROCESS:Landroid/os/ZygoteProcess; 11 +Landroid/media/audiopolicy/AudioVolumeGroup; 11 +Landroid/media/AudioSystem$ErrorCallback; 11 +Landroid/app/servertransaction/ActivityResultItem; 11 +Lcom/android/internal/widget/DialogTitle; 11 +Lcom/android/internal/os/StoragedUidIoStatsReader$Callback; 11 +Landroid/view/ViewRootImpl$W; 11 +Landroid/app/ServiceStartArgs; 11 +Landroid/window/TaskAppearedInfo; 11 +Lcom/android/internal/listeners/ListenerExecutor$FailureCallback; 11 +Landroid/app/ApplicationExitInfo; 11 +Landroid/content/pm/PackageManager;.sCacheAutoCorker:Landroid/app/PropertyInvalidatedCache$AutoCorker;.mLock:Ljava/lang/Object; 11 +Lcom/android/internal/util/Parcelling$BuiltIn$ForInternedStringValueMap; 11 +Landroid/content/pm/ResolveInfo; 11 +Lcom/android/internal/display/BrightnessSynchronizer; 11 +Landroid/window/IOnBackInvokedCallback$Stub$Proxy; 12 +Landroid/graphics/drawable/PictureDrawable; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.126:Ljava/lang/Byte; 13 +Landroid/view/ViewDebug$ExportedProperty; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.41:Ljava/lang/Byte; 13 +Landroid/view/inputmethod/DeleteGesture; 13 +Landroid/view/ViewDebug$IntToString; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.56:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.65:Ljava/lang/Byte; 13 +Landroid/webkit/WebViewFactory;.sProviderLock:Ljava/lang/Object; 13 +Ljava/lang/IllegalAccessError; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.51:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.52:Ljava/lang/Byte; 13 +Landroid/view/inputmethod/DeleteRangeGesture; 13 +Landroid/window/WindowContext; 13 +Ljava/util/concurrent/ConcurrentSkipListMap$Node; 13 +Landroid/view/inputmethod/SelectRangeGesture; 13 +Landroid/util/MalformedJsonException; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.131:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.120:Ljava/lang/Byte; 13 +Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 13 +Ljava/nio/file/StandardOpenOption;.TRUNCATE_EXISTING:Ljava/nio/file/StandardOpenOption; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.121:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.16:Ljava/lang/Byte; 13 +Ljava/util/concurrent/ConcurrentSkipListMap$Index; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.139:Ljava/lang/Byte; 13 +Landroid/view/ViewDebug$FlagToString; 13 +Landroid/view/inputmethod/SelectGesture; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.20:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.94:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.64:Ljava/lang/Byte; 13 +Landroid/webkit/WebViewFactoryProvider$Statics; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.95:Ljava/lang/Byte; 13 +Landroid/service/media/MediaBrowserService$ServiceBinder$1; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.7:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.23:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.46:Ljava/lang/Byte; 13 +Landroid/provider/Settings$SettingNotFoundException; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.74:Ljava/lang/Byte; 13 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.8:Ljava/lang/Byte; 13 +Landroid/widget/TextView;.TEMP_POSITION:[F 13 +Ljava/io/ByteArrayInputStream; 14 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.93:Ljava/lang/Byte; 14 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.134:Ljava/lang/Byte; 14 +Landroid/text/style/ImageSpan; 14 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.154:Ljava/lang/Byte; 15 +Landroid/view/TextureView$SurfaceTextureListener; 16 +Landroid/media/AudioManager$OnAudioFocusChangeListener; 17 +Ljava/util/Locale;.JAPAN:Ljava/util/Locale; 18 +Ljava/util/Locale;.GERMANY:Ljava/util/Locale; 19 +Ljava/util/Locale;.CANADA_FRENCH:Ljava/util/Locale; 20 +Ljava/util/Locale;.ITALY:Ljava/util/Locale; 20 +Ljava/util/Locale;.FRANCE:Ljava/util/Locale; 20 +Ljava/util/Locale;.UK:Ljava/util/Locale; 21 +Ljava/util/Locale;.CANADA:Ljava/util/Locale; 21 +Ljava/util/Locale$Cache;.LOCALECACHE:Ljava/util/Locale$Cache;.map:Ljava/util/concurrent/ConcurrentMap; 22 +Ljava/lang/IllegalStateException; 23 +Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sMessageCallbacksPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool; 24 +Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sMessageCallbacksPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool;.mPool:[Ljava/lang/Object; 24 +Landroid/media/MediaRouter$WifiDisplayStatusChangedReceiver; 25 +Landroid/media/MediaRouter$VolumeChangeReceiver; 25 +Landroid/app/AppOpsManager$OnOpActiveChangedListener; 26 +Landroid/media/PlayerBase; 27 +Landroid/content/pm/Checksum$Type; 28 +Ljava/lang/Class; 29 +Landroid/widget/MediaController$MediaPlayerControl; 30 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.135:Ljava/lang/Long; 30 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.152:Ljava/lang/Long; 30 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.215:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.206:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.137:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.203:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.213:Ljava/lang/Byte; 31 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.549:Ljava/lang/Long; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.201:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.249:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.163:Ljava/lang/Byte; 31 +Ljava/util/HashMap; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.210:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.161:Ljava/lang/Byte; 31 +Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.0:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.199:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.248:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.252:Ljava/lang/Byte; 31 +Lcom/android/ims/rcs/uce/UceDeviceState;.DEVICE_STATE_DESCRIPTION:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.3:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.159:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.217:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.200:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.240:Ljava/lang/Byte; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.198:Ljava/lang/Byte; 31 +Lcom/android/ims/rcs/uce/UceDeviceState;.DEVICE_STATE_DESCRIPTION:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.4:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 31 +Landroid/content/pm/PackageManager$OnChecksumsReadyListener; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.193:Ljava/lang/Byte; 31 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.228:Ljava/lang/Long; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.236:Ljava/lang/Byte; 31 +Landroid/telephony/ims/ImsService;.CAPABILITIES_LOG_MAP:Ljava/util/Map;.table:[Ljava/lang/Object;.2:Ljava/lang/Long; 31 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.211:Ljava/lang/Byte; 31 +Landroid/view/SurfaceView; 32 +Landroid/view/ViewStub$OnInflateListener; 33 +Landroid/graphics/drawable/DrawableInflater;.CONSTRUCTOR_MAP:Ljava/util/HashMap; 34 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.245:Ljava/lang/Byte; 35 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.232:Ljava/lang/Byte; 35 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.12:Ljava/lang/Byte; 35 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.170:Ljava/lang/Long; 35 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.183:Ljava/lang/Long; 35 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.246:Ljava/lang/Byte; 35 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.168:Ljava/lang/Long; 35 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.72:Ljava/lang/Byte; 35 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.243:Ljava/lang/Byte; 35 +Ljava/util/WeakHashMap;.NULL_KEY:Ljava/lang/Object; 35 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.235:Ljava/lang/Byte; 35 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.147:Ljava/lang/Long; 35 +Ljava/io/InterruptedIOException; 35 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.184:Ljava/lang/Long; 35 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.165:Ljava/lang/Long; 35 +Landroid/text/style/ForegroundColorSpan; 35 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.176:Ljava/lang/Long; 35 +Ljava/lang/Long$LongCache;.archivedCache:[Ljava/lang/Long;.173:Ljava/lang/Long; 35 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.181:Ljava/lang/Byte; 35 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.157:Ljava/lang/Byte; 35 +Landroid/content/res/AssetManager$AssetInputStream; 35 +Landroid/graphics/drawable/TransitionDrawable; 36 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1:Ljava/lang/Boolean; 37 +Landroid/view/ViewOverlay$OverlayViewGroup; 38 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.11:Ljava/lang/Boolean; 39 +Ljava/util/Observer; 40 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.129:Ljava/lang/Byte; 41 +[Ljava/lang/Byte; 41 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.144:Ljava/lang/Byte; 41 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.164:Ljava/lang/Byte; 42 +Landroid/view/OrientationEventListener; 43 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.195:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.233:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.229:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.128:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.242:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.196:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.208:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.212:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.228:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.205:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.197:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.204:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.207:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.223:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.244:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.174:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.194:Ljava/lang/Byte; 44 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.225:Ljava/lang/Byte; 45 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.239:Ljava/lang/Byte; 45 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.238:Ljava/lang/Byte; 45 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.227:Ljava/lang/Byte; 45 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.152:Ljava/lang/Byte; 46 +Landroid/app/RemoteAction; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.168:Ljava/lang/Byte; 46 +Landroid/text/style/QuoteSpan; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.54:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.124:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.142:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.190:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.114:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.69:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.30:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.133:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.49:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.58:Ljava/lang/Byte; 46 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.143:Ljava/lang/Byte; 47 +Landroid/icu/text/RelativeDateTimeFormatter$AbsoluteUnit; 47 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.82:Ljava/lang/Byte; 47 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.140:Ljava/lang/Byte; 47 +Landroid/icu/text/RelativeDateTimeFormatter;.fallbackCache:[Landroid/icu/text/RelativeDateTimeFormatter$Style; 47 +Landroid/icu/text/RelativeDateTimeFormatter$Style; 47 +Landroid/icu/text/RelativeDateTimeFormatter;.cache:Landroid/icu/text/RelativeDateTimeFormatter$Cache;.cache:Landroid/icu/impl/CacheBase;.map:Ljava/util/concurrent/ConcurrentHashMap; 47 +Landroid/icu/text/RelativeDateTimeFormatter$RelativeUnit; 47 +Landroid/icu/text/RelativeDateTimeFormatter$Direction; 47 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.130:Ljava/lang/Byte; 47 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.43:Ljava/lang/Byte; 47 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.146:Ljava/lang/Byte; 47 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.138:Ljava/lang/Byte; 47 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.136:Ljava/lang/Byte; 48 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.0:Ljava/lang/Byte; 49 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.160:Ljava/lang/Byte; 49 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.169:Ljava/lang/Byte; 50 +Landroid/widget/Spinner; 50 +Landroid/widget/MultiAutoCompleteTextView; 50 +Ljava/util/ArrayList; 50 +Landroid/widget/CheckBox; 50 +Ljava/io/Serializable; 50 +Landroid/widget/RatingBar; 50 +Ljava/lang/Byte$ByteCache;.archivedCache:[Ljava/lang/Byte;.132:Ljava/lang/Byte; 50 +Landroid/widget/AutoCompleteTextView; 50 +Ljava/util/concurrent/ConcurrentLinkedDeque$Node; 50 +[Ljava/lang/Object; 50 +Landroid/widget/SeekBar; 51 +Ljava/lang/Void; 52 +Landroid/app/ActivityTaskManager;.sInstance:Landroid/util/Singleton; 53 +Landroid/view/ViewRootImpl$$ExternalSyntheticLambda11; 54 +Landroid/view/ViewTreeObserver$OnWindowFocusChangeListener; 55 +Landroid/view/InsetsAnimationThread; 56 +Lcom/android/internal/jank/InteractionJankMonitor$InstanceHolder; 57 +Lcom/android/internal/jank/InteractionJankMonitor; 57 +Landroid/hardware/camera2/CameraCharacteristics;.FLASH_INFO_AVAILABLE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 58 +Landroid/hardware/display/NightDisplayListener$Callback; 59 +Landroid/media/MediaRouter2Manager; 59 +Landroid/os/HandlerExecutor; 59 +Landroid/os/strictmode/LeakedClosableViolation; 60 +Lcom/android/internal/logging/MetricsLogger; 60 +Lcom/android/internal/os/PowerProfile;.sPowerItemMap:Ljava/util/HashMap; 61 +Lcom/android/internal/os/PowerProfile;.sPowerArrayMap:Ljava/util/HashMap; 61 +Lcom/android/internal/os/PowerProfile;.sModemPowerProfile:Lcom/android/internal/power/ModemPowerProfile;.mPowerConstants:Landroid/util/SparseDoubleArray;.mValues:Landroid/util/SparseLongArray; 61 +Landroid/content/IntentFilter; 62 +Landroid/telecom/TelecomManager; 63 +Ljava/lang/IllegalArgumentException; 64 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object; 65 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.1:Ljava/util/WeakHashMap$Entry;.referent:Ljava/lang/Object;.mCache:Ljava/util/LinkedHashMap; 65 +Landroid/telephony/VisualVoicemailSmsFilterSettings;.DEFAULT_ORIGINATING_NUMBERS:Ljava/util/List; 66 +Ljava/util/AbstractList; 68 +Ljava/util/AbstractCollection; 68 +Ljava/util/Collections$EmptyList; 69 +Ljava/lang/StackTraceElement; 69 +[Ljava/lang/StackTraceElement; 69 +Landroid/os/strictmode/Violation; 70 +Ljava/util/List; 71 +Ljava/lang/String; 72 +Ljava/io/ObjectInputStream; 73 +Ljava/io/ObjectStreamClass$Caches;.localDescs:Ljava/util/concurrent/ConcurrentMap; 73 +Ljava/io/ObjectStreamClass$Caches;.reflectors:Ljava/util/concurrent/ConcurrentMap; 73 +Ljava/io/ObjectOutputStream; 73 +Ljava/lang/Number; 74 +Ljava/math/BigInteger; 75 +[B 76 +Landroid/os/Handler; 77 +Landroid/view/accessibility/AccessibilityManager; 78 +Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray;.mKeys:[I 79 +Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 79 +Landroid/view/ViewConfiguration;.sConfigurations:Landroid/util/SparseArray; 79 +Landroid/widget/FrameLayout; 80 +Lcom/android/internal/inputmethod/ImeTracing; 80 +Lcom/android/internal/policy/DecorView; 80 +Landroid/view/accessibility/AccessibilityNodeIdManager; 80 +Landroid/view/ViewTreeObserver; 80 +Landroid/view/ViewRootImpl; 80 +Landroid/os/SystemProperties;.sChangeCallbacks:Ljava/util/ArrayList; 80 +Landroid/transition/ChangeTransform; 80 +Landroid/window/SurfaceSyncGroup; 80 +Landroid/transition/ChangeClipBounds; 80 +Landroid/view/SurfaceControlRegistry; 80 +Landroid/transition/ChangeImageTransform; 80 +Landroid/widget/LinearLayout; 80 +Landroid/view/ViewStub; 81 +Landroid/text/TextLine;.sCached:[Landroid/text/TextLine; 82 +Landroid/text/TextUtils; 82 +Landroid/graphics/TemporaryBuffer; 82 +Landroid/content/res/ColorStateList;.sCache:Landroid/util/SparseArray; 83 +Landroid/text/Layout;.sTempRect:Landroid/graphics/Rect; 84 +Landroid/widget/ImageView; 85 +Landroid/graphics/drawable/ColorDrawable; 86 +Landroid/os/StrictMode$InstanceTracker;.sInstanceCounts:Ljava/util/HashMap; 87 +Landroid/app/ActivityClient;.INTERFACE_SINGLETON:Landroid/app/ActivityClient$ActivityClientControllerSingleton; 88 +Landroid/app/ActivityClient;.sInstance:Landroid/util/Singleton; 88 +Landroid/view/AbsSavedState$1; 89 +Landroid/app/FragmentManagerState; 90 +Landroid/window/OnBackAnimationCallback; 91 +Landroid/animation/AnimatorInflater;.sTmpTypedValue:Landroid/util/TypedValue; 92 +Landroid/graphics/drawable/RippleDrawable; 93 +Landroid/view/inputmethod/IInputMethodManagerGlobalInvoker; 94 +Landroid/app/ActivityTaskManager;.IActivityTaskManagerSingleton:Landroid/util/Singleton; 95 +Landroid/view/Choreographer; 96 +Lcom/android/internal/os/SomeArgs; 97 +Landroid/graphics/Bitmap; 98 +Landroid/view/autofill/AutofillId; 99 +Landroid/view/inputmethod/BaseInputConnection;.COMPOSING:Ljava/lang/Object; 100 +Landroid/text/Selection;.SELECTION_MEMORY:Ljava/lang/Object; 101 +Landroid/text/Selection;.SELECTION_END:Ljava/lang/Object; 101 +Landroid/text/Selection;.SELECTION_START:Ljava/lang/Object; 101 +Landroid/text/SpannableStringBuilder;.sCachedIntBuffer:[[I 102 +Landroid/text/Selection$MemoryTextWatcher; 103 +Landroid/text/SpanWatcher; 104 +Lcom/android/internal/util/ArrayUtils;.sCache:[Ljava/lang/Object; 105 +Ljava/lang/Integer;.SMALL_NEG_VALUES:[Ljava/lang/String; 106 +Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 107 +Lsun/nio/ch/SharedFileLockTable;.lockMap:Ljava/util/concurrent/ConcurrentHashMap; 108 +Lsun/nio/ch/FileChannelImpl; 108 +Landroid/database/sqlite/SQLiteDatabase$CursorFactory; 109 +Landroid/database/sqlite/SQLiteDebug$NoPreloadHolder; 110 +Landroid/database/sqlite/SQLiteCompatibilityWalFlags; 110 +Landroid/database/sqlite/SQLiteGlobal; 110 +Landroid/database/CursorWindow; 111 +Landroid/content/ContentResolver; 112 +Ljava/nio/charset/Charset; 113 +Landroid/app/ContextImpl; 114 +Ljava/util/concurrent/Executors$DefaultThreadFactory;.poolNumber:Ljava/util/concurrent/atomic/AtomicInteger; 115 +Landroid/content/pm/PackageManager;.sPackageInfoCache:Landroid/app/PropertyInvalidatedCache;.mCache:Ljava/util/LinkedHashMap; 116 +Landroid/content/pm/PackageManager;.sApplicationInfoCache:Landroid/app/PropertyInvalidatedCache;.mCache:Ljava/util/LinkedHashMap; 117 +Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.systemContext:Ljava/util/logging/LogManager$LoggerContext;.namedLoggers:Ljava/util/Hashtable;.table:[Ljava/util/Hashtable$HashtableEntry; 118 +Landroid/ddm/DdmHandleAppName; 118 +Landroid/provider/DeviceConfigInitializer; 118 +Lsun/misc/Cleaner; 118 +Ldalvik/system/CloseGuard; 118 +Landroid/graphics/Typeface; 118 +Landroid/os/BinderProxy;.sProxyMap:Landroid/os/BinderProxy$ProxyMap;.mMainIndexKeys:[[Ljava/lang/Long; 118 +Landroid/permission/PermissionManager; 118 +Landroid/media/MediaFrameworkPlatformInitializer; 118 +Ljava/util/TimeZone; 118 +Landroid/os/Environment; 118 +Landroid/compat/Compatibility; 118 +Landroid/os/ServiceManager; 118 +Landroid/content/pm/PackageManager;.sApplicationInfoCache:Landroid/app/PropertyInvalidatedCache; 118 +Ljava/util/Locale$NoImagePreloadHolder; 118 +Ljava/lang/System; 118 +Lcom/android/internal/os/RuntimeInit; 118 +Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.systemContext:Ljava/util/logging/LogManager$LoggerContext;.namedLoggers:Ljava/util/Hashtable; 118 +Ldalvik/system/VMRuntime;.THE_ONE:Ldalvik/system/VMRuntime; 118 +Landroid/view/View; 118 +Landroid/hardware/display/DisplayManagerGlobal; 118 +Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager; 118 +Landroid/telephony/TelephonyFrameworkInitializer; 118 +Landroid/se/omapi/SeFrameworkInitializer; 118 +Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap;.mHashes:[I 118 +Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder; 118 +Landroid/security/net/config/ApplicationConfig; 118 +Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map; 118 +Ljava/util/Locale; 118 +Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map;.table:[Ljava/util/WeakHashMap$Entry; 118 +Ljava/security/Provider; 118 +Ldalvik/system/ZygoteHooks; 118 +Landroid/os/Message; 118 +Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object; 118 +Landroid/app/LoadedApk;.sApplications:Landroid/util/ArrayMap; 118 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap; 118 +Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.userContext:Ljava/util/logging/LogManager$LoggerContext;.namedLoggers:Ljava/util/Hashtable; 118 +Ljava/lang/ThreadGroup;.mainThreadGroup:Ljava/lang/ThreadGroup; 118 +Ldalvik/system/RuntimeHooks; 118 +Landroid/nfc/NfcFrameworkInitializer; 118 +Landroid/os/Looper; 118 +Landroid/os/LocaleList; 118 +Ldalvik/system/SocketTagger; 118 +Landroid/icu/util/TimeZone; 118 +Landroid/util/ArraySet; 118 +Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.systemContext:Ljava/util/logging/LogManager$LoggerContext;.root:Ljava/util/logging/LogManager$LogNode; 118 +Landroid/os/BinderProxy;.sProxyMap:Landroid/os/BinderProxy$ProxyMap;.mMainIndexValues:[Ljava/util/ArrayList; 118 +Ljava/util/Random;.seedUniquifier:Ljava/util/concurrent/atomic/AtomicLong; 118 +Landroid/app/ActivityThread; 118 +Landroid/os/Binder; 118 +Ljava/lang/ThreadLocal;.nextHashCode:Ljava/util/concurrent/atomic/AtomicInteger; 119 +Landroid/os/Parcel; 120 +Landroid/system/UnixSocketAddress; 120 +Ljava/lang/ThreadGroup;.systemThreadGroup:Ljava/lang/ThreadGroup; 120 +Ljava/lang/Daemons$FinalizerDaemon;.INSTANCE:Ljava/lang/Daemons$FinalizerDaemon; 120 +Landroid/os/Parcel;.sPairedCreators:Ljava/util/HashMap; 120 +Ljava/lang/Thread; 120 +Landroid/os/Parcel;.mCreators:Ljava/util/HashMap; 120 +Ljava/lang/Daemons$FinalizerDaemon;.INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;.progressCounter:Ljava/util/concurrent/atomic/AtomicInteger; 120 +Landroid/system/StructPollfd; 120 +Ljava/lang/Daemons$HeapTaskDaemon;.INSTANCE:Ljava/lang/Daemons$HeapTaskDaemon; 120 +Landroid/system/StructTimeval; 120 +Ldalvik/system/VMRuntime;.THE_ONE:Ldalvik/system/VMRuntime;.allocationCount:Ljava/util/concurrent/atomic/AtomicInteger; 120 +Ljava/lang/Daemons$ReferenceQueueDaemon;.INSTANCE:Ljava/lang/Daemons$ReferenceQueueDaemon;.progressCounter:Ljava/util/concurrent/atomic/AtomicInteger; 120 +Landroid/os/GraphicsEnvironment;.sInstance:Landroid/os/GraphicsEnvironment; 120 +Ljava/lang/Daemons$FinalizerWatchdogDaemon;.INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon; 120 +Ljava/lang/ref/FinalizerReference; 120 +Landroid/os/Process; 120 +Ljava/lang/Daemons$ReferenceQueueDaemon;.INSTANCE:Ljava/lang/Daemons$ReferenceQueueDaemon; 120 +Lcom/android/internal/os/BinderInternal; 120 +Landroid/app/ApplicationLoaders;.gApplicationLoaders:Landroid/app/ApplicationLoaders;.mLoaders:Landroid/util/ArrayMap; 121 +Landroid/app/DexLoadReporter;.INSTANCE:Landroid/app/DexLoadReporter;.mDataDirs:Ljava/util/Set;.map:Ljava/util/HashMap; 122 +Ldalvik/system/BaseDexClassLoader; 122 +Landroid/renderscript/RenderScriptCacheDir; 122 +Landroid/graphics/Compatibility; 123 +Llibcore/io/Libcore; 123 +Landroid/provider/FontsContract; 123 +Ljava/security/Security;.version:Ljava/util/concurrent/atomic/AtomicInteger; 123 +Llibcore/net/NetworkSecurityPolicy; 123 +Lsun/security/jca/Providers; 123 +Landroid/graphics/Canvas; 123 +Landroid/os/StrictMode; 124 +Landroid/content/pm/PackageManager;.sPackageInfoCache:Landroid/app/PropertyInvalidatedCache; 125 +Lcom/android/internal/os/StatsdHiddenApiUsageLogger;.sInstance:Lcom/android/internal/os/StatsdHiddenApiUsageLogger; 126 +Ljava/util/logging/LogManager;.manager:Ljava/util/logging/LogManager;.loggerRefQueue:Ljava/lang/ref/ReferenceQueue; 127 +Landroid/view/WindowManagerGlobal; 128 +Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool; 129 +Lcom/android/internal/util/function/pooled/PooledLambdaImpl;.sPool:Lcom/android/internal/util/function/pooled/PooledLambdaImpl$Pool;.mPool:[Ljava/lang/Object; 129 +Landroid/view/inputmethod/InputMethodManager; 130 +Landroid/media/MediaRouter; 131 +Landroid/hardware/SensorPrivacyManager; 132 +Landroid/os/storage/StorageManager; 133 +Landroid/view/contentcapture/ContentCaptureManager; 134 +Landroid/hardware/input/InputManager; 134 +Landroid/app/people/PeopleManager; 134 +Landroid/media/session/MediaSessionManager; 134 +Landroid/security/attestationverification/AttestationVerificationManager; 134 +Landroid/net/vcn/VcnManager; 134 +Landroid/os/RecoverySystem; 134 +Landroid/net/NetworkPolicyManager; 134 +Landroid/net/wifi/sharedconnectivity/app/SharedConnectivityManager; 134 +Landroid/permission/PermissionControllerManager; 134 +Landroid/app/tare/EconomyManager; 134 +Landroid/view/translation/TranslationManager; 134 +Landroid/view/textclassifier/TextClassificationManager; 134 +Landroid/view/autofill/AutofillManager; 134 +Landroid/os/SystemConfigManager; 134 +Landroid/view/LayoutInflater; 134 +Landroid/credentials/CredentialManager; 134 +Landroid/service/persistentdata/PersistentDataBlockManager; 134 +Landroid/view/textservice/TextServicesManager; 134 +Landroid/app/admin/DevicePolicyManager; 134 +Ljava/lang/StackStreamFactory; 134 +Landroid/view/WindowManager; 134 +Landroid/app/contentsuggestions/ContentSuggestionsManager; 134 +Landroid/media/tv/tunerresourcemanager/TunerResourceManager; 134 +Landroid/telephony/SubscriptionManager; 134 +Landroid/os/HardwarePropertiesManager; 134 +Landroid/media/AudioManager; 135 +Landroid/telephony/TelephonyManager; 136 +Landroid/util/ArrayMap; 137 +Landroid/app/QueuedWork; 138 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.0:Ljava/util/WeakHashMap$Entry; 139 +Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap; 140 +Ljava/util/concurrent/ScheduledThreadPoolExecutor;.sequencer:Ljava/util/concurrent/atomic/AtomicLong; 141 +Landroid/util/Log; 142 +Ljava/util/Collections$SynchronizedCollection; 143 +Ljava/util/Set; 143 +Ljava/util/Collections$SynchronizedSet; 143 +Ljava/util/Collection; 143 +Ljava/lang/Integer;.SMALL_NONNEG_VALUES:[Ljava/lang/String; 144 +Landroid/content/ComponentName; 145 +Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle; 146 +Landroid/os/PersistableBundle;.EMPTY:Landroid/os/PersistableBundle; 147 +Landroid/icu/impl/locale/BaseLocale;.CACHE:Landroid/icu/impl/locale/BaseLocale$Cache;._map:Ljava/util/concurrent/ConcurrentHashMap; 148 +Ljava/util/GregorianCalendar; 149 +Ljava/text/DontCareFieldPosition;.INSTANCE:Ljava/text/FieldPosition; 150 +Landroid/app/UiModeManager; 151 +Ljdk/internal/access/SharedSecrets; 152 +Landroid/icu/impl/ZoneMeta;.CANONICAL_ID_CACHE:Landroid/icu/impl/ICUCache; 153 +Landroid/icu/impl/ZoneMeta;.SYSTEM_ZONE_CACHE:Landroid/icu/impl/ZoneMeta$SystemTimeZoneCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 154 +Ljava/time/ZoneOffset;.ID_CACHE:Ljava/util/concurrent/ConcurrentMap; 155 +Ljava/time/ZoneOffset;.ID_CACHE:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node; 155 +Ljava/time/ZoneOffset;.SECONDS_CACHE:Ljava/util/concurrent/ConcurrentMap; 155 +Ljava/time/ZoneOffset;.SECONDS_CACHE:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.0:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node; 155 +Ljava/time/ZoneOffset;.SECONDS_CACHE:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node; 155 +Landroid/widget/TextView; 156 +Landroid/view/ViewGroup$ChildListForAutoFillOrContentCapture;.sPool:Landroid/util/Pools$SimplePool;.mPool:[Ljava/lang/Object; 157 +Landroid/view/ViewGroup$ChildListForAutoFillOrContentCapture;.sPool:Landroid/util/Pools$SimplePool; 157 +Landroid/view/ViewGroup; 158 +Landroid/graphics/Rect; 159 +Landroid/view/View$BaseSavedState; 160 +Landroid/widget/Button; 161 +Landroid/widget/ImageButton; 162 +Landroid/view/View$OnHoverListener; 163 +Landroid/widget/Toolbar; 164 +Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal; 165 +Landroid/app/WallpaperManager; 166 +Landroid/graphics/ColorSpace$Model;.RGB:Landroid/graphics/ColorSpace$Model; 166 +Landroid/graphics/drawable/AdaptiveIconDrawable; 167 +Landroid/animation/ValueAnimator$DurationScaleChangeListener; 168 +Landroid/widget/Toast; 168 +Landroid/app/smartspace/SmartspaceSession$OnTargetsAvailableListener; 168 +Landroid/view/CrossWindowBlurListeners; 168 +Landroid/app/servertransaction/ActivityRelaunchItem; 169 +[Ljava/util/concurrent/ForkJoinTask; 169 +Landroid/view/WindowManager$LayoutParams; 169 +Ljava/util/concurrent/ForkJoinPool$WorkQueue; 169 +Landroid/app/prediction/AppTargetEvent; 169 +Lorg/xmlpull/v1/XmlPullParserException; 169 +Landroid/app/servertransaction/ObjectPool;.sPoolMap:Ljava/util/Map; 170 +Landroid/app/servertransaction/ClientTransaction; 170 +Landroid/app/servertransaction/StopActivityItem; 170 +Landroid/system/ErrnoException; 171 +Landroid/hardware/location/ContextHubTransaction$OnCompleteListener; 172 +Landroid/app/PendingIntent$OnFinished; 172 +Ljava/lang/NullPointerException; 173 +Landroid/os/strictmode/DiskReadViolation; 174 +Lorg/apache/http/params/HttpParams; 175 +Landroid/nfc/cardemulation/CardEmulation; 176 +Ljava/io/FileDescriptor; 177 +Landroid/content/pm/PackageManager$OnPermissionsChangedListener; 178 +Landroid/security/keystore2/KeyStoreCryptoOperationUtils; 179 +Landroid/app/ActivityTaskManager; 180 +Landroid/util/EventLog; 181 +Ljava/net/URLConnection; 181 +Ljava/net/SocketException; 181 +Ljava/lang/reflect/InvocationTargetException; 181 +Ljava/lang/Enum; 182 +Landroid/widget/AbsListView$SelectionBoundsAdjuster; 183 +Ljava/lang/ClassNotFoundException; 183 +Landroid/content/SyncStatusObserver; 184 +Landroid/content/AsyncTaskLoader$LoadTask; 185 +Landroid/app/LoaderManager$LoaderCallbacks; 185 +Landroid/webkit/CookieSyncManager; 186 +Landroid/webkit/WebViewProvider$ViewDelegate; 187 +Landroid/webkit/WebView; 187 +Landroid/webkit/WebViewProvider$ScrollDelegate; 187 +Landroid/webkit/WebViewProvider; 187 +Landroid/webkit/WebViewFactory;.sTimestamps:Landroid/webkit/WebViewFactory$StartupTimestamps; 188 +Landroid/webkit/WebViewFactoryProvider; 189 +Landroid/webkit/WebViewFactory; 190 +Landroid/os/PowerManager$OnThermalStatusChangedListener; 191 +Landroid/os/Bundle; 192 +Landroid/widget/ProgressBar; 193 +Landroid/graphics/Bitmap$Config;.ALPHA_8:Landroid/graphics/Bitmap$Config; 194 +Landroid/graphics/Bitmap$Config;.RGB_565:Landroid/graphics/Bitmap$Config; 194 +Landroid/graphics/Bitmap$Config;.RGBA_1010102:Landroid/graphics/Bitmap$Config; 194 +Landroid/renderscript/Allocation;.mBitmapOptions:Landroid/graphics/BitmapFactory$Options;.inPreferredConfig:Landroid/graphics/Bitmap$Config; 194 +Landroid/graphics/Bitmap$Config;.RGBA_F16:Landroid/graphics/Bitmap$Config; 194 +Landroid/graphics/Bitmap$Config;.ARGB_4444:Landroid/graphics/Bitmap$Config; 194 +Landroid/graphics/Bitmap$Config;.HARDWARE:Landroid/graphics/Bitmap$Config; 194 +Landroid/graphics/drawable/StateListDrawable; 195 +Landroid/view/PointerIcon;.gSystemIconsByDisplay:Landroid/util/SparseArray; 196 +Landroid/view/PointerIcon; 196 +Ljavax/net/ssl/SSLServerSocketFactory; 197 +Ljavax/net/ssl/SSLSocketFactory; 198 +Ljavax/net/ssl/HttpsURLConnection$NoPreloadHolder; 198 +Ljavax/net/ssl/SSLSessionContext; 199 +Lcom/android/org/bouncycastle/crypto/CryptoServicesRegistrar; 200 +Lsun/security/x509/PKIXExtensions;.KeyUsage_Id:Lsun/security/util/ObjectIdentifier; 201 +Lsun/security/x509/PKIXExtensions;.PolicyConstraints_Id:Lsun/security/util/ObjectIdentifier; 201 +Ljava/security/cert/PKIXRevocationChecker$Option;.ONLY_END_ENTITY:Ljava/security/cert/PKIXRevocationChecker$Option; 201 +Lsun/security/x509/PKIXExtensions;.ExtendedKeyUsage_Id:Lsun/security/util/ObjectIdentifier; 201 +Lsun/security/provider/X509Factory;.certCache:Lsun/security/util/Cache; 201 +Lsun/security/x509/PKIXExtensions;.CertificatePolicies_Id:Lsun/security/util/ObjectIdentifier; 201 +Lsun/security/x509/PKIXExtensions;.NameConstraints_Id:Lsun/security/util/ObjectIdentifier; 201 +Lsun/security/x509/PKIXExtensions;.AuthorityKey_Id:Lsun/security/util/ObjectIdentifier; 201 +Lsun/security/provider/X509Factory;.certCache:Lsun/security/util/Cache;.cacheMap:Ljava/util/Map; 201 +Ljava/security/cert/PKIXRevocationChecker$Option;.NO_FALLBACK:Ljava/security/cert/PKIXRevocationChecker$Option; 201 +Lsun/security/x509/PKIXExtensions;.SubjectAlternativeName_Id:Lsun/security/util/ObjectIdentifier; 201 +Lsun/security/x509/PKIXExtensions;.PolicyMappings_Id:Lsun/security/util/ObjectIdentifier; 202 +Lsun/security/x509/PKIXExtensions;.InhibitAnyPolicy_Id:Lsun/security/util/ObjectIdentifier; 202 +Lsun/security/x509/PKIXExtensions;.BasicConstraints_Id:Lsun/security/util/ObjectIdentifier; 202 +Ljava/security/Security;.spiMap:Ljava/util/Map; 203 +Lsun/security/x509/X500Name;.commonName_oid:Lsun/security/util/ObjectIdentifier; 204 +Lsun/security/x509/X500Name;.countryName_oid:Lsun/security/util/ObjectIdentifier; 204 +Lsun/security/x509/X500Name;.orgName_oid:Lsun/security/util/ObjectIdentifier; 204 +Ljava/nio/charset/Charset;.cache2:Ljava/util/HashMap; 205 +Ljava/net/URL;.handlers:Ljava/util/Hashtable;.table:[Ljava/util/Hashtable$HashtableEntry; 206 +Ljava/net/URL;.handlers:Ljava/util/Hashtable; 206 +Ljava/net/Inet6AddressImpl;.addressCache:Ljava/net/AddressCache;.cache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap; 207 +Ljava/net/Proxy$Type;.DIRECT:Ljava/net/Proxy$Type; 208 +Ljava/net/ProxySelector;.theProxySelector:Ljava/net/ProxySelector; 208 +Lcom/android/okhttp/okio/SegmentPool; 209 +Lcom/android/okhttp/internal/http/AuthenticatorAdapter;.INSTANCE:Lcom/android/okhttp/Authenticator; 209 +Lcom/android/okhttp/HttpsHandler;.HTTP_1_1_ONLY:Ljava/util/List;.element:Ljava/lang/Object; 209 +Lcom/android/okhttp/ConfigAwareConnectionPool;.instance:Lcom/android/okhttp/ConfigAwareConnectionPool;.networkEventDispatcher:Llibcore/net/event/NetworkEventDispatcher;.listeners:Ljava/util/List; 210 +Lcom/android/okhttp/Dns;.SYSTEM:Lcom/android/okhttp/Dns; 210 +Lcom/android/okhttp/ConfigAwareConnectionPool;.instance:Lcom/android/okhttp/ConfigAwareConnectionPool; 210 +Lcom/android/okhttp/okio/AsyncTimeout; 211 +Ljava/lang/IllegalAccessException; 212 +Ljavax/net/ssl/SSLContext; 213 +Ljavax/net/ssl/HttpsURLConnection; 213 +Ljava/security/Security;.props:Ljava/util/Properties;.map:Ljava/util/concurrent/ConcurrentHashMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.12:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node; 214 +Ljava/security/Security;.props:Ljava/util/Properties;.map:Ljava/util/concurrent/ConcurrentHashMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.30:Ljava/util/concurrent/ConcurrentHashMap$Node; 214 +Landroid/database/sqlite/SQLiteTransactionListener; 215 +Landroid/accounts/OnAccountsUpdateListener; 216 +Landroid/accounts/AccountManager$20; 217 +Lsun/nio/ch/FileChannelImpl$Unmapper; 218 +Landroid/content/res/Resources;.sResourcesHistory:Ljava/util/Set;.c:Ljava/util/Collection;.m:Ljava/util/Map;.queue:Ljava/lang/ref/ReferenceQueue; 219 +Landroid/text/method/SingleLineTransformationMethod; 220 +Landroid/widget/RelativeLayout; 221 +Landroid/graphics/drawable/BitmapDrawable; 222 +Landroid/graphics/drawable/GradientDrawable; 223 +Landroid/animation/PropertyValuesHolder;.sGetterPropertyMap:Ljava/util/HashMap; 224 +Landroid/animation/PropertyValuesHolder$FloatPropertyValuesHolder;.sJNISetterPropertyMap:Ljava/util/HashMap; 225 +Landroid/graphics/drawable/Drawable;.DEFAULT_TINT_MODE:Landroid/graphics/PorterDuff$Mode; 226 +Landroid/text/StaticLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool; 227 +Landroid/text/StaticLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 227 +Ljava/util/concurrent/ThreadLocalRandom; 228 +Landroid/widget/Space; 229 +Landroid/widget/ScrollView; 230 +Landroid/text/style/LineHeightSpan; 231 +Landroid/text/style/TabStopSpan; 232 +Landroid/text/style/ReplacementSpan; 233 +Landroid/text/style/MetricAffectingSpan; 233 +Landroid/text/style/LeadingMarginSpan; 233 +Landroid/text/style/LineBackgroundSpan; 234 +Landroid/text/style/CharacterStyle; 235 +Landroid/text/style/SuggestionSpan; 236 +Landroid/widget/TextView$ChangeWatcher; 237 +Landroid/text/DynamicLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 238 +Landroid/text/DynamicLayout; 238 +Landroid/text/DynamicLayout$ChangeWatcher; 238 +Landroid/text/style/WrapTogetherSpan; 238 +Landroid/text/DynamicLayout$Builder;.sPool:Landroid/util/Pools$SynchronizedPool; 238 +Landroid/text/method/LinkMovementMethod; 239 +Landroid/text/style/ClickableSpan; 240 +Ljava/util/logging/LogRecord;.globalSequenceNumber:Ljava/util/concurrent/atomic/AtomicLong; 241 +Ljava/lang/Runtime;.currentRuntime:Ljava/lang/Runtime; 242 +Landroid/content/pm/LauncherActivityInfo; 243 +Landroid/database/sqlite/SQLiteMisuseException; 243 +Landroid/speech/tts/TextToSpeech$Connection$SetupConnectionAsyncTask; 243 +Landroid/database/sqlite/SQLiteCantOpenDatabaseException; 243 +Landroid/database/sqlite/SQLiteDatabaseCorruptException; 243 +Landroid/database/sqlite/SQLiteDatabaseLockedException; 243 +Ljava/util/Map$Entry; 243 +Ljava/util/zip/ZipException; 243 +Landroid/database/sqlite/SQLiteAccessPermException; 243 +Landroid/speech/tts/TextToSpeech$OnInitListener; 243 +Landroid/app/Notification$MessagingStyle; 244 +Landroid/text/TextUtils$TruncateAt; 245 +Landroid/app/smartspace/SmartspaceTarget; 246 +Landroid/app/prediction/AppTarget; 246 +Landroid/app/smartspace/uitemplatedata/BaseTemplateData; 246 +Landroid/location/LocationManager;.sLocationListeners:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry; 247 +Landroid/location/LocationManager;.sLocationListeners:Ljava/util/WeakHashMap; 247 +Landroid/service/notification/ConditionProviderService; 248 +Landroid/os/WorkSource; 249 +Landroid/security/keystore2/AndroidKeyStoreProvider; 249 +Ljava/net/Socket; 249 +Lcom/android/internal/listeners/ListenerTransport; 249 +Landroid/os/ParcelUuid; 250 +Landroid/telephony/emergency/EmergencyNumber; 251 +Lcom/android/internal/telephony/uicc/UiccProfile$4; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$IncomingSms; 251 +Lcom/android/internal/telephony/SmsStorageMonitor$1; 251 +Lcom/android/internal/telephony/TelephonyDevController; 251 +Lcom/android/internal/telephony/uicc/UiccController; 251 +Lcom/android/internal/telephony/emergency/EmergencyNumberTracker$1; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$DataCallSession; 251 +Lcom/android/internal/telephony/TelephonyDevController;.mSims:Ljava/util/ArrayList; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsAcsProvisioningStats; 251 +Ljava/lang/UnsupportedOperationException; 251 +Landroid/database/CursorToBulkCursorAdaptor; 251 +Lcom/android/internal/telephony/satellite/PointingAppController; 251 +Landroid/telephony/ModemActivityInfo; 251 +Lcom/android/internal/telephony/imsphone/ImsPhone; 251 +Lcom/android/internal/telephony/ServiceStateTracker; 251 +Lcom/android/internal/telephony/IccSmsInterfaceManager; 251 +Lcom/android/internal/telephony/util/NotificationChannelController$1; 251 +Lcom/android/internal/telephony/RilWakelockInfo; 251 +Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler$GsmCbTestBroadcastReceiver; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportFeatureTagStats; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$UceEventStats; 251 +Lcom/android/internal/telephony/euicc/EuiccConnector$BindingState; 251 +Lcom/android/internal/telephony/ims/ImsResolver$3; 251 +Landroid/net/NetworkPolicyManager$SubscriptionCallbackProxy; 251 +Lcom/android/internal/telephony/TelephonyDevController;.mModems:Ljava/util/ArrayList; 251 +Landroid/telephony/ims/aidl/IImsServiceController$Stub$Proxy; 251 +Lcom/android/internal/telephony/CarrierPrivilegesTracker$1; 251 +Lcom/android/internal/telephony/CommandException; 251 +Lcom/android/ims/FeatureConnector$1; 251 +Lcom/android/internal/telephony/IWapPushManager; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipDelegateStats; 251 +Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mTtyModeReceivedRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251 +Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mMmiCompleteRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251 +Lcom/android/i18n/timezone/TelephonyLookup; 251 +Landroid/telephony/BarringInfo$BarringServiceInfo; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequests; 251 +Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState$14; 251 +Lcom/android/internal/telephony/SmsBroadcastUndelivered; 251 +Lcom/android/internal/telephony/LocaleTracker; 251 +Lcom/android/internal/telephony/PhoneSubInfoController; 251 +Lcom/android/internal/telephony/CarrierKeyDownloadManager$1; 251 +Lcom/android/internal/telephony/GsmCdmaCallTracker$1; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.339:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/ServiceStateTracker$1; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.353:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/euicc/EuiccCardController$SimSlotStatusChangedBroadcastReceiver; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$PresenceNotifyEvent; 251 +Lcom/android/internal/telephony/SimActivationTracker$1; 251 +Landroid/telephony/ModemInfo; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1393:[Ljava/lang/String; 251 +Landroid/telephony/CellSignalStrengthWcdma; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteProvision; 251 +Lcom/android/internal/telephony/PhoneConfigurationManager; 251 +Lcom/android/internal/telephony/SmsApplication$SmsPackageMonitor; 251 +Landroid/telephony/TelephonyRegistryManager$3; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallSession; 251 +Landroid/os/Handler$MessengerImpl; 251 +Lcom/android/internal/telephony/LocaleTracker$1; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularDataServiceSwitch; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mInCallVoicePrivacyOffRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSosMessageRecommender; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationServiceDescStats; 251 +Lcom/android/internal/telephony/uicc/UiccPkcs15$Pkcs15Selector; 251 +Lcom/android/internal/telephony/CarrierResolver$2; 251 +Lcom/android/internal/telephony/CarrierActionAgent$1; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mPhones:Ljava/util/ArrayList; 251 +Lcom/android/internal/telephony/SmsController; 251 +Lcom/android/internal/telephony/uicc/euicc/EuiccCardException; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationTermination; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1125:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/NetworkTypeController$1; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.803:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/uicc/asn1/TagNotFoundException; 251 +Lcom/android/internal/telephony/CarrierServiceBindHelper$1; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularServiceState; 251 +Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager; 251 +Lcom/android/internal/telephony/InboundSmsHandler$NewMessageNotificationActionReceiver; 251 +Lcom/android/internal/telephony/CarrierActionAgent; 251 +Lcom/android/i18n/timezone/TimeZoneFinder; 251 +Lcom/android/internal/telephony/RILRequest; 251 +Lcom/android/internal/telephony/RIL;.sRilTimeHistograms:Landroid/util/SparseArray; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.33:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/MccTable; 251 +Lcom/android/internal/telephony/uicc/UiccProfile$2; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierIdMismatch; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1235:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/uicc/UiccCarrierPrivilegeRules; 251 +Landroid/telephony/CellSignalStrengthTdscdma; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerListenerEvent; 251 +Lcom/android/internal/telephony/SmsDispatchersController; 251 +Landroid/timezone/TelephonyLookup; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteOutgoingDatagram; 251 +Lcom/android/internal/telephony/SMSDispatcher$1; 251 +Lcom/android/internal/telephony/AppSmsManager; 251 +Landroid/timezone/TimeZoneFinder; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mBackgroundCalls:Ljava/util/ArrayList; 251 +Lcom/android/ims/rcs/uce/eab/EabProvider; 251 +Lcom/android/internal/telephony/uicc/PinStorage$1; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerEvent; 251 +Landroid/telephony/CellSignalStrengthLte; 251 +Landroid/telephony/ims/ProvisioningManager$Callback$CallbackBinder; 251 +Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray;.mKeys:[I 251 +Landroid/telephony/CellSignalStrengthNr; 251 +Lcom/android/internal/telephony/SomeArgs; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mInCallVoicePrivacyOnRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251 +Lcom/android/internal/telephony/StateMachine$SmHandler; 251 +Lcom/android/internal/telephony/PackageChangeReceiver; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingShortCodeSms; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequestsV2; 251 +Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall; 251 +Lcom/android/internal/telephony/euicc/EuiccConnector$AvailableState; 251 +Lcom/android/ims/internal/IImsServiceFeatureCallback$Stub$Proxy; 251 +Landroid/telephony/data/ApnSetting;.APN_TYPE_INT_MAP:Ljava/util/Map; 251 +Lcom/android/internal/telephony/RadioInterfaceCapabilityController; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationStats; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallRatUsage; 251 +Lcom/android/internal/telephony/metrics/TelephonyMetrics; 251 +Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall; 251 +Lcom/android/internal/telephony/NetworkRegistrationManager$NetworkRegStateCallback; 251 +Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler; 251 +Lcom/android/internal/telephony/euicc/EuiccConnector$ConnectedState; 251 +Lcom/android/internal/telephony/RadioConfig; 251 +Lcom/android/internal/telephony/euicc/EuiccConnector$DisconnectedState; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSession; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mDisplayInfoRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251 +Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo; 251 +Lcom/android/internal/telephony/GsmCdmaPhone; 251 +Lcom/android/internal/telephony/TelephonyTester$1; 251 +Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler$CdmaScpTestBroadcastReceiver; 251 +Lcom/android/internal/telephony/NetworkTypeController$DefaultState; 251 +Landroid/net/TelephonyNetworkSpecifier; 251 +Lcom/android/internal/telephony/NitzStateMachine; 251 +Landroid/app/timezonedetector/TimeZoneDetector; 251 +Lcom/android/internal/telephony/IntentBroadcaster$1; 251 +Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher; 251 +Lcom/android/internal/telephony/ims/ImsResolver$1; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingSms; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$UnmeteredNetworks; 251 +Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader;.packages:Ljava/util/Map;.m:Ljava/util/Map; 251 +Lcom/android/internal/telephony/euicc/EuiccController; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mForegroundCalls:Ljava/util/ArrayList; 251 +Lcom/android/internal/telephony/satellite/SatelliteModemInterface; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.531:[Ljava/lang/String; 251 +Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray; 251 +Lcom/android/internal/telephony/euicc/EuiccConnector$1; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.467:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipMessageResponse; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportSession; 251 +Lcom/android/internal/telephony/euicc/EuiccConnector$UnavailableState; 251 +Lcom/android/internal/telephony/DeviceStateMonitor$3; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mSignalInfoRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251 +Lcom/android/ims/ImsManager;.IMS_STATS_CALLBACKS:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 251 +Lcom/android/internal/telephony/IccPhoneBookInterfaceManager; 251 +Lcom/android/internal/telephony/DisplayInfoController; 251 +Lcom/android/internal/telephony/ims/ImsResolver$2; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.1377:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyServiceState$NetworkRegistrationInfo; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteController; 251 +Landroid/telephony/ims/RegistrationManager$RegistrationCallback$RegistrationBinder; 251 +Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$EmergencyNumbersInfo; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$GbaEvent; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.23:[Ljava/lang/String; 251 +Landroid/telephony/CellSignalStrengthCdma; 251 +Landroid/telephony/TelephonyLocalConnection; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteIncomingDatagram; 251 +Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker$2; 251 +Lcom/android/internal/telephony/ims/ImsResolver; 251 +Lcom/android/internal/telephony/SmsStorageMonitor; 251 +Lcom/android/internal/telephony/uicc/UiccProfile; 251 +Landroid/telephony/ims/ImsMmTelManager$CapabilityCallback$CapabilityBinder; 251 +Lcom/android/internal/telephony/euicc/EuiccCardController; 251 +Lcom/android/internal/telephony/SmsBroadcastUndelivered$1; 251 +Lcom/android/internal/telephony/GsmCdmaCallTracker; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsClientProvisioningStats; 251 +Lcom/android/internal/telephony/cat/CatService; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.761:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/SmsApplication; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mDisconnectRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251 +Lcom/android/internal/telephony/PhoneFactory; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mHandlerMap:Ljava/util/HashMap; 251 +Landroid/os/AsyncResult; 251 +Lcom/android/internal/telephony/ProxyController; 251 +Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler$CdmaCbTestBroadcastReceiver; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.453:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/MultiSimSettingController; 251 +Ljava/io/BufferedReader; 251 +Landroid/telephony/CellSignalStrengthGsm; 251 +Lcom/android/internal/telephony/SimActivationTracker; 251 +Lcom/android/internal/telephony/CellBroadcastServiceManager; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mRingingCalls:Ljava/util/ArrayList; 251 +Lcom/android/internal/telephony/IntentBroadcaster; 251 +Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationFeatureTagStats; 251 +Lcom/android/internal/telephony/euicc/EuiccConnector$EuiccPackageMonitor; 251 +Lcom/android/internal/telephony/CallManager;.INSTANCE:Lcom/android/internal/telephony/CallManager;.mSuppServiceFailedRegistrants:Lcom/android/internal/telephony/RegistrantList;.registrants:Ljava/util/ArrayList; 251 +Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mClassLoader:Ljava/lang/ClassLoader;.packages:Ljava/util/Map;.m:Ljava/util/Map;.table:[Ljava/util/HashMap$Node; 251 +Lcom/android/internal/telephony/CarrierServiceBindHelper$CarrierServicePackageMonitor; 251 +Lcom/android/internal/telephony/TelephonyComponentFactory; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.637:[Ljava/lang/String; 251 +Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo; 251 +Landroid/telephony/CarrierConfigManager;.sDefaults:Landroid/os/PersistableBundle;.mMap:Landroid/util/ArrayMap;.mArray:[Ljava/lang/Object;.651:[Ljava/lang/String; 251 +Lcom/android/internal/telephony/SmsUsageMonitor; 251 +Lcom/android/internal/telephony/CommandException$Error;.INVALID_SIM_STATE:Lcom/android/internal/telephony/CommandException$Error; 251 +Landroid/hardware/display/DisplayManagerGlobal$DisplayManagerCallback; 252 +Landroid/app/ActivityThread$ApplicationThread; 252 +Landroid/app/ActivityManager$MyUidObserver; 253 +Landroid/media/browse/MediaBrowser$ServiceCallbacks; 253 +Landroid/media/session/MediaController$CallbackStub; 253 +Landroid/media/session/MediaSessionManager$OnMediaKeyEventSessionChangedListener; 253 +Landroid/app/PendingIntent$CancelListener; 253 +Landroid/media/AudioManager$2; 253 +Landroid/database/ContentObserver$Transport; 253 +Landroid/media/session/MediaSessionManager$SessionsChangedWrapper$1; 253 +Landroid/content/ContentProvider$PipeDataWriter; 254 +Landroid/security/net/config/UserCertificateSource$NoPreloadHolder; 255 +Landroid/view/Window$Callback; 256 +Landroid/transition/TransitionManager;.sDefaultTransition:Landroid/transition/Transition;.mTransitions:Ljava/util/ArrayList;.elementData:[Ljava/lang/Object;.1:Landroid/transition/ChangeBounds;.mCurrentAnimators:Ljava/util/ArrayList; 256 +Landroid/transition/TransitionManager;.sDefaultTransition:Landroid/transition/Transition;.mTransitions:Ljava/util/ArrayList;.elementData:[Ljava/lang/Object;.2:Landroid/transition/Fade;.mCurrentAnimators:Ljava/util/ArrayList; 256 +Landroid/transition/TransitionManager;.sPendingTransitions:Ljava/util/ArrayList; 256 +Landroid/transition/TransitionManager;.sDefaultTransition:Landroid/transition/Transition;.mTransitions:Ljava/util/ArrayList;.elementData:[Ljava/lang/Object;.0:Landroid/transition/Fade;.mCurrentAnimators:Ljava/util/ArrayList; 256 +Landroid/view/AttachedSurfaceControl$OnBufferTransformHintChangedListener; 257 +Landroid/webkit/ValueCallback; 258 +Landroid/webkit/WebResourceRequest; 258 +Landroid/webkit/WebChromeClient$CustomViewCallback; 258 +Landroid/hardware/camera2/CameraCharacteristics;.INFO_SUPPORTED_HARDWARE_LEVEL:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 258 +Landroid/accounts/Account; 258 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 258 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/params/StreamConfigurationDuration; 259 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_STALL_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.JPEGR_AVAILABLE_JPEG_R_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/params/HighSpeedVideoConfiguration; 259 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/CameraCharacteristics;.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +I 259 +Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_CAPABILITIES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 259 +Landroid/hardware/camera2/params/StreamConfiguration; 259 +Z 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PIXEL_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.LENS_FOCAL_LENGTH:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_APERTURES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.DISTORTION_CORRECTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.EXTENSION_STRENGTH:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +J 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PHYSICAL_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.TONEMAP_PRESET_CURVE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +B 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_TRIGGER:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_EXPOSURE_COMPENSATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +[D 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_SESSION_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_EXPOSURE_TIME:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_CALIBRATION_TRANSFORM2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_TRANSFORM:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/content/res/Resources$Theme; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +F 260 +Landroid/hardware/camera2/CaptureRequest;.LENS_FILTER_DENSITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.LENS_OPTICAL_STABILIZATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.NOISE_REDUCTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_FRAME_DURATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_EXTENDED_SCENE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.STATISTICS_OIS_DATA_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_TEST_PATTERN_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.HOT_PIXEL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_ANTIBANDING_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.STATISTICS_LENS_SHADING_MAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.SCALER_CROP_REGION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.LENS_FOCUS_DISTANCE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.TONEMAP_GAMMA:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_REFERENCE_ILLUMINANT2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_REFERENCE_ILLUMINANT1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +[F 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_ZOOM_RATIO:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_ABERRATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.TONEMAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.REQUEST_AVAILABLE_REQUEST_KEYS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.SCALER_ROTATE_AND_CROP:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_GAINS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_COLOR_TRANSFORM1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_SENSITIVITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_INFO_AVAILABLE_FOCAL_LENGTHS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_OPTICAL_BLACK_REGIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.JPEG_QUALITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.FLASH_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_POST_RAW_SENSITIVITY_BOOST:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_WHITE_LEVEL:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_SETTINGS_OVERRIDE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +[Landroid/hardware/camera2/params/MeteringRectangle; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_INFO_EXPOSURE_TIME_RANGE:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Ljava/lang/Float; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_ENABLE_ZSL:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.INFO_DEVICE_STATE_ORIENTATIONS:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_CALIBRATION_TRANSFORM1:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.EDGE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_CAPTURE_INTENT:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_ORIENTATION:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.JPEG_ORIENTATION:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CameraCharacteristics;.SENSOR_COLOR_TRANSFORM2:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +[J 260 +Landroid/hardware/camera2/CameraCharacteristics;.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Ljava/util/concurrent/Phaser; 260 +Landroid/hardware/camera2/CaptureRequest;.BLACK_LEVEL_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.COLOR_CORRECTION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_SCENE_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.JPEG_THUMBNAIL_SIZE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.SHADING_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.STATISTICS_FACE_DETECT_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.STATISTICS_HOT_PIXEL_MAP_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AUTOFRAMING:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_TARGET_FPS_RANGE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AWB_LOCK:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_TEST_PATTERN_DATA:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AE_PRECAPTURE_TRIGGER:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.FLASH_STRENGTH_LEVEL:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_VIDEO_STABILIZATION_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Ljava/lang/Boolean; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_EFFECT_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.LENS_APERTURE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.JPEG_THUMBNAIL_QUALITY:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Landroid/hardware/camera2/CaptureRequest;.CONTROL_AF_REGIONS:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +Ljava/lang/Long; 260 +Landroid/hardware/camera2/CaptureRequest;.SENSOR_PIXEL_MODE:Landroid/hardware/camera2/CaptureRequest$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 260 +[Ljava/lang/String; 261 +[Z 262 +Ljava/lang/Class$Caches;.genericInterfaces:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap; 263 +Ljava/util/Map; 264 +Ljava/nio/Bits; 265 +Ljava/nio/DirectByteBuffer; 266 +Ljava/io/File; 267 +Ljava/nio/ByteBuffer; 268 +Ljava/io/InputStream; 269 +Landroid/os/ParcelFileDescriptor; 270 +Landroid/os/BinderProxy;.sProxyMap:Landroid/os/BinderProxy$ProxyMap; 271 +Landroid/app/PendingIntent; 272 +Landroid/content/Intent; 273 +Landroid/net/Uri$HierarchicalUri; 274 +Landroid/net/Uri$StringUri; 275 +Landroid/net/Uri$PathPart;.EMPTY:Landroid/net/Uri$PathPart; 276 +Lcom/android/internal/telephony/MccTable;.FALLBACKS:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.6:Ljava/util/HashMap$Node;.key:Ljava/lang/Object; 277 +Landroid/icu/text/DecimalFormatSymbols;.cachedLocaleData:Landroid/icu/impl/CacheBase;.map:Ljava/util/concurrent/ConcurrentHashMap; 278 +Llibcore/icu/DecimalFormatData;.CACHE:Ljava/util/concurrent/ConcurrentHashMap; 279 +Landroid/icu/impl/CurrencyData;.provider:Landroid/icu/impl/CurrencyData$CurrencyDisplayInfoProvider; 280 +Lcom/android/internal/infra/AndroidFuture; 281 +Lcom/android/internal/util/LatencyTracker$Action; 282 +Landroid/app/AppOpsManager$Mode; 283 +Landroid/view/accessibility/AccessibilityManager$AccessibilityServicesStateChangeListener; 284 +Landroid/annotation/IdRes; 285 +Landroid/content/pm/PackageItemInfo; 286 +Ljava/util/Random; 287 +Landroid/widget/RadioButton; 288 +Lcom/android/internal/policy/PhoneWindow$PanelFeatureState$SavedState; 289 +Landroid/graphics/Insets; 290 +Landroid/view/View;.sNextGeneratedId:Ljava/util/concurrent/atomic/AtomicInteger; 291 +Landroid/graphics/drawable/LayerDrawable; 292 +Landroid/animation/LayoutTransition; 293 +Llibcore/reflect/AnnotationFactory;.cache:Ljava/util/Map; 294 +Llibcore/reflect/AnnotationFactory;.cache:Ljava/util/Map;.table:[Ljava/util/WeakHashMap$Entry; 294 +Ljava/lang/reflect/Proxy;.proxyClassCache:Ljava/lang/reflect/WeakCache;.reverseMap:Ljava/util/concurrent/ConcurrentMap; 295 +Ljava/lang/reflect/Proxy$ProxyClassFactory;.nextUniqueNumber:Ljava/util/concurrent/atomic/AtomicLong; 295 +Ljava/lang/reflect/Proxy;.proxyClassCache:Ljava/lang/reflect/WeakCache;.map:Ljava/util/concurrent/ConcurrentMap; 296 +Ljava/lang/Object; 297 +Ljava/lang/invoke/MethodType;.internTable:Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet;.map:Ljava/util/concurrent/ConcurrentMap; 298 +Ljava/nio/channels/SocketChannel;.dexCache:Ljava/lang/Object; 298 +Ljava/lang/invoke/MethodType;.objectOnlyTypes:[Ljava/lang/invoke/MethodType; 299 +Ljava/util/concurrent/ForkJoinTask; 300 +Ljava/util/concurrent/CompletableFuture; 301 +Landroid/app/Notification$BigTextStyle; 302 +Landroid/content/pm/ApplicationInfo; 303 +Ljava/security/Signature;.signatureInfo:Ljava/util/Map;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.13:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node; 304 +Lsun/security/x509/X500Name;.stateName_oid:Lsun/security/util/ObjectIdentifier; 305 +Lsun/security/x509/X500Name;.localityName_oid:Lsun/security/util/ObjectIdentifier; 306 +Lsun/security/x509/X500Name;.orgUnitName_oid:Lsun/security/util/ObjectIdentifier; 306 +Ljava/util/UUID; 307 +Landroid/app/slice/Slice; 308 +Ljava/util/Locale;.FRENCH:Ljava/util/Locale; 308 +Landroid/os/NullVibrator; 308 +Ldalvik/system/CloseGuard;.MESSAGE:Ljava/lang/String; 308 +Lsun/util/locale/BaseLocale$Cache;.CACHE:Lsun/util/locale/BaseLocale$Cache;.map:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.22:Ljava/util/concurrent/ConcurrentHashMap$Node;.val:Ljava/lang/Object;.referent:Ljava/lang/Object; 308 +Ljava/util/Locale$Cache;.LOCALECACHE:Ljava/util/Locale$Cache;.map:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.24:Ljava/util/concurrent/ConcurrentHashMap$Node;.val:Ljava/lang/Object;.referent:Ljava/lang/Object; 308 +Landroid/app/Activity$$ExternalSyntheticLambda0; 308 +Landroid/icu/impl/locale/BaseLocale;.CACHE:Landroid/icu/impl/locale/BaseLocale$Cache;._map:Ljava/util/concurrent/ConcurrentHashMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.0:Ljava/util/concurrent/ConcurrentHashMap$Node; 308 +Ljava/util/Locale;.ITALIAN:Ljava/util/Locale; 308 +Landroid/media/MediaRouter2Manager$Callback; 308 +Lsun/util/locale/BaseLocale$Cache;.CACHE:Lsun/util/locale/BaseLocale$Cache;.map:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.29:Ljava/util/concurrent/ConcurrentHashMap$Node;.val:Ljava/lang/Object;.referent:Ljava/lang/Object; 308 +Ljava/util/Locale;.GERMAN:Ljava/util/Locale; 309 +Landroid/icu/impl/StandardPlural; 310 +Landroid/icu/impl/number/range/StandardPluralRanges; 311 +Landroid/icu/impl/PluralRulesLoader;.loader:Landroid/icu/impl/PluralRulesLoader; 311 +Landroid/icu/impl/PluralRulesLoader;.loader:Landroid/icu/impl/PluralRulesLoader;.pluralRulesCache:Ljava/util/Map; 311 +Landroid/icu/text/PluralRules$Operand; 311 +Landroid/icu/util/Calendar;.PATTERN_CACHE:Landroid/icu/impl/ICUCache; 312 +Landroid/icu/impl/DateNumberFormat;.CACHE:Landroid/icu/impl/SimpleCache; 313 +Landroid/text/format/DateFormat; 314 +Landroid/view/View$OnDragListener; 315 +Landroid/hardware/input/InputManager$InputDeviceListener; 316 +Landroid/hardware/input/InputManagerGlobal; 317 +Landroid/hardware/SystemSensorManager; 318 +Lcom/android/internal/os/BackgroundThread; 319 +Ljava/lang/Throwable; 320 +Landroid/app/NotificationManager; 321 +Landroid/app/NotificationChannel; 322 +Landroid/content/SharedPreferences$OnSharedPreferenceChangeListener; 323 +Landroid/content/pm/VersionedPackage; 324 +Landroid/app/AppOpsManager; 325 +Ldalvik/system/ZipPathValidator; 326 +Landroid/content/pm/PackageManager;.sPackageInfoCache:Landroid/app/PropertyInvalidatedCache;.mSkips:[J 327 +Landroid/content/pm/PackageManager;.sApplicationInfoCache:Landroid/app/PropertyInvalidatedCache;.mSkips:[J 328 +Lsun/util/locale/BaseLocale$Cache;.CACHE:Lsun/util/locale/BaseLocale$Cache;.map:Ljava/util/concurrent/ConcurrentMap; 329 +Landroid/content/Context; 330 +Ljava/util/concurrent/Executor; 331 +Ljava/util/concurrent/ScheduledExecutorService; 332 +Ljava/util/concurrent/ExecutorService; 332 +Landroid/view/Window$OnFrameMetricsAvailableListener; 333 +Ljava/lang/annotation/Annotation; 334 +Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 335 +Ljava/util/concurrent/CancellationException; 336 +Ljava/lang/NoSuchMethodException; 337 +Landroid/os/strictmode/CustomViolation; 338 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.3:Ljava/util/WeakHashMap$Entry; 339 +Lcom/android/internal/policy/PhoneWindow; 340 +Landroid/view/autofill/AutofillValue; 340 +Landroid/widget/TextView$SavedState; 341 +Landroid/text/method/MetaKeyKeyListener;.SYM:Ljava/lang/Object; 342 +Landroid/text/method/MetaKeyKeyListener;.ALT:Ljava/lang/Object; 342 +Landroid/text/method/MetaKeyKeyListener;.SELECTING:Ljava/lang/Object; 342 +Landroid/text/method/MetaKeyKeyListener;.CAP:Ljava/lang/Object; 342 +Landroid/widget/PopupWindow$PopupBackgroundView; 343 +Landroid/widget/TextView;.TEMP_RECTF:Landroid/graphics/RectF; 343 +Landroid/text/method/ScrollingMovementMethod; 343 +Landroid/icu/impl/locale/UnicodeLocaleExtension;.EMPTY_SORTED_SET:Ljava/util/SortedSet;.m:Ljava/util/NavigableMap; 343 +Landroid/widget/PopupWindow$PopupDecorView; 343 +Landroid/widget/Editor$TextRenderNode; 343 +Landroid/widget/Editor$PositionListener; 344 +Landroid/text/style/SpellCheckSpan; 345 +Landroid/text/method/ArrowKeyMovementMethod; 346 +Landroid/text/method/TextKeyListener;.sInstance:[Landroid/text/method/TextKeyListener; 346 +Landroid/text/TextUtils$TruncateAt;.MARQUEE:Landroid/text/TextUtils$TruncateAt; 347 +Landroid/view/autofill/Helper; 348 +Lcom/android/internal/util/LatencyTracker; 349 +Lcom/android/internal/util/LatencyTracker$SLatencyTrackerHolder; 349 +Landroid/graphics/drawable/Icon; 350 +Landroid/text/style/AlignmentSpan; 351 +Landroid/text/MeasuredParagraph;.sPool:Landroid/util/Pools$SynchronizedPool;.mPool:[Ljava/lang/Object; 352 +Landroid/text/MeasuredParagraph;.sPool:Landroid/util/Pools$SynchronizedPool; 352 +Landroid/icu/impl/ICUResourceBundleReader;.CACHE:Landroid/icu/impl/ICUResourceBundleReader$ReaderCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 353 +Landroid/location/ILocationManager$Stub;.dexCache:Ljava/lang/Object; 354 +Landroid/annotation/CurrentTimeMillisLong; 355 +Ljava/lang/reflect/Method; 356 +Lcom/android/internal/os/ZygoteInit; 356 +Landroid/database/DatabaseUtils; 356 +Landroid/os/HandlerThread; 356 +Ljava/security/Signature;.signatureInfo:Ljava/util/Map;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node; 357 +Ljava/security/Signature;.signatureInfo:Ljava/util/Map; 358 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.queue:Ljava/lang/ref/ReferenceQueue; 359 +Landroid/telephony/TelephonyRegistryManager; 360 +Landroid/graphics/HardwareRenderer; 361 +Landroid/os/BinderProxy; 362 +Landroid/app/compat/CompatChanges;.QUERY_CACHE:Landroid/app/compat/ChangeIdStateCache;.mCache:Ljava/util/LinkedHashMap; 363 +Landroid/app/compat/CompatChanges;.QUERY_CACHE:Landroid/app/compat/ChangeIdStateCache; 363 +Landroid/app/AlarmManager; 364 +Landroid/net/metrics/DhcpClientEvent; 365 +[I 366 +Landroid/media/MediaCodecList; 367 +Landroid/graphics/drawable/InsetDrawable; 368 +Landroid/widget/ProgressBar$SavedState; 369 +Landroid/widget/ScrollView$SavedState; 370 +Landroid/graphics/drawable/AnimatedVectorDrawable; 371 +Landroid/widget/ListView; 372 +Landroid/widget/AbsListView; 373 +Landroid/widget/AbsListView$SavedState; 373 +Landroid/widget/CompoundButton$SavedState; 374 +Landroid/widget/HorizontalScrollView$SavedState; 375 +Landroid/widget/HorizontalScrollView; 376 +Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.1:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 377 +Landroid/icu/text/MeasureFormat;.hmsTo012:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.6:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 377 +Landroid/view/View$OnSystemUiVisibilityChangeListener; 378 +Ljava/util/AbstractMap; 379 +Landroid/telephony/euicc/EuiccCardManager$ResultCallback; 380 +Ljava/lang/Character$UnicodeBlock;.CJK_SYMBOLS_AND_PUNCTUATION:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.KANBUN:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.HANGUL_COMPATIBILITY_JAMO:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.KATAKANA:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.HANGUL_SYLLABLES:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.ENCLOSED_CJK_LETTERS_AND_MONTHS:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.HANGUL_JAMO:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.BOPOMOFO_EXTENDED:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.CJK_COMPATIBILITY_FORMS:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.BOPOMOFO:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.HIRAGANA:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.HALFWIDTH_AND_FULLWIDTH_FORMS:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.KANGXI_RADICALS:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.CJK_RADICALS_SUPPLEMENT:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.KATAKANA_PHONETIC_EXTENSIONS:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.CJK_COMPATIBILITY:Ljava/lang/Character$UnicodeBlock; 381 +Ljava/lang/Character$UnicodeBlock;.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:Ljava/lang/Character$UnicodeBlock; 382 +Ljava/lang/Character$UnicodeBlock;.CJK_COMPATIBILITY_IDEOGRAPHS:Ljava/lang/Character$UnicodeBlock; 382 +Ljava/lang/Character$UnicodeBlock;.CJK_UNIFIED_IDEOGRAPHS:Ljava/lang/Character$UnicodeBlock; 382 +Ljava/lang/Character$UnicodeBlock;.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:Ljava/lang/Character$UnicodeBlock; 382 +Ljava/lang/Character$UnicodeBlock;.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:Ljava/lang/Character$UnicodeBlock; 382 +Lcom/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry; 383 +Landroid/view/Window$DecorCallback; 383 +Landroid/view/inputmethod/EditorInfo; 383 +Landroid/view/MenuItem$OnActionExpandListener; 384 +Ljava/util/Locale;.JAPANESE:Ljava/util/Locale; 385 +Ljava/util/Locale;.KOREAN:Ljava/util/Locale; 385 +Lcom/android/internal/config/appcloning/AppCloningDeviceConfigHelper; 386 +Landroid/telecom/PhoneAccountHandle; 387 +Landroid/content/AsyncQueryHandler; 388 +Landroid/speech/RecognitionListener; 389 +Ljava/lang/InstantiationException; 390 +Ljava/util/concurrent/ExecutionException; 391 +Landroid/icu/text/DateIntervalInfo;.DIICACHE:Landroid/icu/impl/ICUCache; 392 +Landroid/text/format/DateIntervalFormat;.CACHED_FORMATTERS:Landroid/util/LruCache;.map:Ljava/util/LinkedHashMap; 392 +Landroid/icu/text/DateIntervalFormat;.LOCAL_PATTERN_CACHE:Landroid/icu/impl/ICUCache; 392 +Landroid/icu/impl/OlsonTimeZone; 392 +Landroid/text/format/DateIntervalFormat;.CACHED_FORMATTERS:Landroid/util/LruCache; 392 +Landroid/graphics/drawable/Drawable; 393 +Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 394 +Landroid/app/Activity; 395 +Landroid/icu/text/PluralRules$KeywordStatus;.INVALID:Landroid/icu/text/PluralRules$KeywordStatus;.name:Ljava/lang/String; 396 +Landroid/net/Uri; 396 +Lsun/util/calendar/CalendarSystem;.calendars:Ljava/util/concurrent/ConcurrentMap; 396 +Landroid/animation/PropertyValuesHolder$IntPropertyValuesHolder;.sJNISetterPropertyMap:Ljava/util/HashMap; 397 +Landroid/graphics/drawable/ShapeDrawable; 398 +Lcom/android/internal/widget/ActionBarContextView; 399 +Landroid/widget/Toolbar$SavedState; 399 +Lcom/android/internal/widget/ActionBarContainer; 399 +Lcom/android/internal/widget/ActionBarOverlayLayout; 399 +Lcom/android/internal/widget/ActionBarContainer$ActionBarBackgroundDrawable; 399 +Landroid/widget/ActionMenuPresenter$OverflowMenuButton; 400 +Landroid/widget/ActionMenuView; 401 +Landroid/content/res/Configuration; 402 +Ljava/util/IdentityHashMap;.NULL_KEY:Ljava/lang/Object; 403 +Ljava/util/concurrent/ForkJoinPool; 404 +Landroid/os/ResultReceiver; 405 +Ljava/util/concurrent/TimeoutException; 406 +Ljava/io/IOException; 407 +Landroid/accounts/AccountAuthenticatorResponse; 408 +Landroid/nfc/NfcAdapter; 409 +Landroid/nfc/NfcAdapter;.sNfcAdapters:Ljava/util/HashMap; 409 +Landroid/app/backup/BackupManager; 410 +Landroid/app/NotificationChannelGroup; 411 +Landroid/content/pm/ParceledListSlice; 411 +Landroid/os/FileObserver; 412 +Landroid/os/UserHandle; 413 +Landroid/content/pm/PackageManager$NameNotFoundException; 414 +[Ljava/lang/Integer; 415 +Landroid/animation/PropertyValuesHolder;.sSetterPropertyMap:Ljava/util/HashMap; 415 +Landroid/content/LocusId; 416 +Landroid/view/contentcapture/ContentCaptureContext; 416 +Landroid/telephony/ims/RegistrationManager;.IMS_REG_TO_ACCESS_TYPE_MAP:Ljava/util/Map;.table:[Ljava/lang/Object;.18:Ljava/lang/Integer; 417 +Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.4:Ljava/util/concurrent/ConcurrentHashMap$Node; 418 +Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node; 418 +Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.2:Ljava/util/concurrent/ConcurrentHashMap$Node;.next:Ljava/util/concurrent/ConcurrentHashMap$Node; 418 +Lcom/android/internal/telephony/cdnr/CarrierDisplayNameResolver;.EF_SOURCE_PRIORITY:Ljava/util/List;.a:[Ljava/lang/Object;.9:Ljava/lang/Integer; 418 +Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap;.table:[Ljava/util/concurrent/ConcurrentHashMap$Node;.56:Ljava/util/concurrent/ConcurrentHashMap$Node; 418 +Lcom/android/internal/telephony/cdnr/CarrierDisplayNameResolver;.EF_SOURCE_PRIORITY:Ljava/util/List;.a:[Ljava/lang/Object;.5:Ljava/lang/Integer; 418 +Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;.pool:Ljava/util/concurrent/ConcurrentMap; 418 +Landroid/widget/EditText; 419 +Landroid/widget/CheckedTextView; 420 +Landroid/os/strictmode/UnsafeIntentLaunchViolation; 421 +Landroid/app/Service; 422 +Ldalvik/system/BlockGuard; 423 +Landroid/hardware/devicestate/DeviceStateManagerGlobal; 424 +Landroid/hardware/camera2/CameraCharacteristics;.SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 425 +Landroid/hardware/camera2/marshal/MarshalRegistry;.sMarshalerMap:Ljava/util/HashMap; 425 +Landroid/hardware/camera2/CameraCharacteristics;.LENS_FACING:Landroid/hardware/camera2/CameraCharacteristics$Key;.mKey:Landroid/hardware/camera2/impl/CameraMetadataNative$Key; 425 +Landroid/content/ClipboardManager$OnPrimaryClipChangedListener; 426 +Landroid/icu/text/BreakIterator;.iterCache:[Landroid/icu/impl/CacheValue; 427 +Landroid/app/PropertyInvalidatedCache;.sCaches:Ljava/util/WeakHashMap;.table:[Ljava/util/WeakHashMap$Entry;.13:Ljava/util/WeakHashMap$Entry; 428 +Landroid/icu/text/Collator; 429 +Landroid/icu/impl/number/parse/NanMatcher;.DEFAULT:Landroid/icu/impl/number/parse/NanMatcher;.uniSet:Landroid/icu/text/UnicodeSet;.strings:Ljava/util/SortedSet;.c:Ljava/util/Collection;.m:Ljava/util/NavigableMap; 430 +Ljava/io/FileNotFoundException; 431 +Landroid/os/BaseBundle; 432 +Landroid/service/watchdog/ExplicitHealthCheckService$PackageConfig; 433 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.116:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.12:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.90:Ljava/lang/String; 434 +Landroid/icu/text/MessageFormat;.rootLocale:Ljava/util/Locale;.baseLocale:Lsun/util/locale/BaseLocale;.language:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.385:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.107:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.112:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.480:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.550:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.143:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._obsoleteLanguages:[Ljava/lang/String;.1:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.473:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.138:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.204:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.71:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.12:Ljava/lang/String; 434 +Landroid/icu/impl/duration/impl/DataRecord$ETimeLimit;.names:[Ljava/lang/String;.1:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.9:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.99:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages3:[Ljava/lang/String;.152:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.256:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.170:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.220:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.461:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._replacementLanguages:[Ljava/lang/String;.5:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.190:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.157:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._obsoleteCountries:[Ljava/lang/String;.4:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.196:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.117:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.5:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.499:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.199:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.18:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.324:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.101:Ljava/lang/String; 434 +Landroid/icu/impl/locale/UnicodeLocaleExtension;.CA_JAPANESE:Landroid/icu/impl/locale/UnicodeLocaleExtension;._keywords:Ljava/util/SortedMap;.root:Ljava/util/TreeMap$TreeMapEntry;.key:Ljava/lang/Object; 434 +Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.3:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.140:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.105:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.37:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._replacementCountries:[Ljava/lang/String;.5:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.22:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.103:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.412:Ljava/lang/String; 434 +Landroid/icu/impl/duration/impl/DataRecord$EMilliSupport;.names:[Ljava/lang/String;.1:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.124:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.232:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.219:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.179:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.523:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.75:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.486:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.166:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.112:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.119:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.160:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.298:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.257:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.182:Ljava/lang/String; 434 +Landroid/icu/impl/units/UnitPreferences;.measurementSystem:Ljava/util/Map;.m:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.5:Ljava/util/HashMap$Node;.next:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.47:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.180:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.111:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.358:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.96:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._obsoleteLanguages:[Ljava/lang/String;.0:Ljava/lang/String; 434 +Landroid/icu/text/DateFormat;.HOUR_GENERIC_TZ:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.67:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.254:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.222:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.55:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.349:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.16:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.352:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.443:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.478:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.19:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.401:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.137:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.65:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.474:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.168:Ljava/lang/String; 434 +Landroid/icu/impl/units/UnitPreferences;.measurementSystem:Ljava/util/Map;.m:Ljava/util/Map;.table:[Ljava/util/HashMap$Node;.15:Ljava/util/HashMap$Node;.value:Ljava/lang/Object; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.111:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages3:[Ljava/lang/String;.545:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.30:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.469:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.21:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.69:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.56:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.519:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._replacementLanguages:[Ljava/lang/String;.4:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.107:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.290:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.59:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.220:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.186:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.516:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.181:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.199:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.396:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.117:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.227:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.331:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.447:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.151:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._languages:[Ljava/lang/String;.144:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.132:Ljava/lang/String; 434 +Landroid/icu/impl/LocaleIDs;._countries:[Ljava/lang/String;.230:Ljava/lang/String; 434 +Landroid/icu/text/DateFormat;.MINUTE_SECOND:Ljava/lang/String; 434 +Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 435 +Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.head:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 436 +Ljava/lang/Enum;.sharedConstantsCache:Llibcore/util/BasicLruCache;.map:Ljava/util/LinkedHashMap;.tail:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry;.before:Ljava/util/LinkedHashMap$LinkedHashMapEntry; 437 +Landroid/graphics/drawable/ColorStateListDrawable; 438 +Ljava/lang/SecurityException; 439 +Ljava/lang/RuntimeException; 440 +Landroid/media/audiopolicy/AudioProductStrategy; 441 +Landroid/os/PersistableBundle; 442 +Landroid/content/pm/ShortcutInfo; 442 +Landroid/icu/text/TimeZoneFormat;._tzfCache:Landroid/icu/text/TimeZoneFormat$TimeZoneFormatCache;.map:Ljava/util/concurrent/ConcurrentHashMap; 443 +Landroid/graphics/LeakyTypefaceStorage;.sStorage:Ljava/util/ArrayList; 443 +Landroid/graphics/LeakyTypefaceStorage;.sTypefaceMap:Landroid/util/ArrayMap; 443 +Landroid/text/TextWatcher; 444 +Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener; 445 +Ljavax/net/SocketFactory; 446 +Ljava/util/Collections; 447 +Ljava/lang/Exception; 448 +Landroid/os/UserManager; 449 +Landroid/os/RemoteException; 450 +Landroid/content/AttributionSource; 451 +Lcom/android/okhttp/internalandroidapi/HttpURLConnectionFactory$DnsAdapter; 452 +Lcom/android/okhttp/Protocol;.HTTP_2:Lcom/android/okhttp/Protocol; 452 +Ljava/net/Inet4Address; 452 +Lcom/android/okhttp/Protocol;.SPDY_3:Lcom/android/okhttp/Protocol; 452 +Lcom/android/okhttp/OkHttpClient; 452 +Landroid/os/storage/VolumeInfo; 453 +Landroid/os/storage/DiskInfo; 453 +Landroid/view/textclassifier/TextLanguage;.EMPTY:Landroid/view/textclassifier/TextLanguage;.mBundle:Landroid/os/Bundle;.mMap:Landroid/util/ArrayMap; 454 +Ljava/nio/file/StandardOpenOption;.WRITE:Ljava/nio/file/StandardOpenOption; 455 +Ljava/nio/file/StandardOpenOption;.APPEND:Ljava/nio/file/StandardOpenOption; 456 +Ljava/util/logging/FileHandler; 457 +Ljava/nio/file/StandardOpenOption;.CREATE_NEW:Ljava/nio/file/StandardOpenOption; 457 +Ljava/util/logging/FileHandler;.locks:Ljava/util/Set;.map:Ljava/util/HashMap; 457 +Lsun/nio/ch/SharedFileLockTable;.queue:Ljava/lang/ref/ReferenceQueue; 458 +Ljavax/net/ServerSocketFactory; 458 +Landroid/os/AsyncTask; 459 +Landroid/os/strictmode/UnbufferedIoViolation; 460 +Landroid/app/usage/AppStandbyInfo; 461 +Landroid/text/format/DateUtils; 462 +Landroid/security/IKeyChainService; 463 +Landroid/util/Log$TerribleFailure; 464 +Lcom/android/internal/os/RuntimeInit$KillApplicationHandler; 464 +Ljava/util/Timer;.nextSerialNumber:Ljava/util/concurrent/atomic/AtomicInteger; 465 +Landroid/telephony/ims/stub/ImsConfigImplBase$ImsConfigStub; 466 +Landroid/telephony/ims/stub/ImsRegistrationImplBase$1; 466 +Landroid/telephony/ims/ImsUtListener; 466 +Landroid/telephony/ims/feature/MmTelFeature$1; 466 +Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray;.mValues:[Ljava/lang/Object; 467 +Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray; 467 +Lcom/android/ims/ImsManager;.IMS_MANAGER_INSTANCES:Landroid/util/SparseArray;.mKeys:[I 467 +Landroid/telephony/ims/aidl/IImsConfig$Stub$Proxy; 468 +Landroid/telephony/ims/aidl/IImsRegistration$Stub$Proxy; 468 +Landroid/telephony/NetworkService; 469 +Landroid/telephony/TelephonyCallback$ServiceStateListener; 470 +Landroid/telephony/TelephonyCallback$PhysicalChannelConfigListener; 471 +Landroid/telephony/TelephonyCallback$RadioPowerStateListener; 471 +Lsun/security/x509/X500Name;.internedOIDs:Ljava/util/Map; 472 +Lsun/security/x509/X500Name;.internedOIDs:Ljava/util/Map;.table:[Ljava/util/HashMap$Node; 472 +Landroid/media/MediaCodec; 473 +Ljava/nio/file/StandardOpenOption;.CREATE:Ljava/nio/file/StandardOpenOption; 474 +Ljava/nio/file/NoSuchFileException; 475 +Ljava/text/DateFormatSymbols;.cachedInstances:Ljava/util/concurrent/ConcurrentMap; 476 +Ljava/util/Currency;.instances:Ljava/util/concurrent/ConcurrentMap; 476 +Ljava/util/Calendar;.cachedLocaleData:Ljava/util/concurrent/ConcurrentMap; 476 +Ljava/text/SimpleDateFormat;.cachedNumberFormatData:Ljava/util/concurrent/ConcurrentMap; 476 +Landroid/app/UriGrantsManager;.IUriGrantsManagerSingleton:Landroid/util/Singleton; 477 +Landroid/content/ContentProviderProxy; 478 +Landroid/os/DeadObjectException; 479 +Landroid/app/slice/SliceSpec; 479 +Landroid/database/sqlite/SQLiteDatabase; 480 +Ljava/util/Locale;.CHINA:Ljava/util/Locale; 481 +Ljava/util/Locale;.TAIWAN:Ljava/util/Locale; 481 +Ljava/util/Locale;.KOREA:Ljava/util/Locale; 481 +Ljava/util/Scanner; 482 +Ljava/math/BigDecimal; 483 +Ljava/security/interfaces/RSAPrivateCrtKey; 483 +Ljava/security/interfaces/RSAPrivateKey; 483 +Lcom/android/server/backup/AccountSyncSettingsBackupHelper;.KEY_ACCOUNT_TYPE:Ljava/lang/String; 483 +Landroid/util/UtilConfig; 484 +Ljava/net/ResponseCache; 485 +Landroid/content/ReceiverCallNotAllowedException; 486 +Landroid/app/ReceiverRestrictedContext; 487 +Landroid/os/strictmode/CredentialProtectedWhileLockedViolation; 488 +Landroid/app/Application; 489 +Ljava/util/NoSuchElementException; 490 +Landroid/os/Messenger; 491 +Landroid/telephony/TelephonyCallback$DataEnabledListener; 491 +Landroid/system/StructLinger; 492 diff --git a/core/api/current.txt b/core/api/current.txt index 7f261d450b42..d1d798346c97 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -428,6 +428,7 @@ package android { field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8 field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9 + field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final int adServiceTypes; field public static final int addPrintersActivity = 16843750; // 0x10103e6 field public static final int addStatesFromChildren = 16842992; // 0x10100f0 field public static final int adjustViewBounds = 16843038; // 0x101011e @@ -12606,6 +12607,7 @@ package android.content.pm { method public boolean isStagedSessionApplied(); method public boolean isStagedSessionFailed(); method public boolean isStagedSessionReady(); + method @FlaggedApi("android.content.pm.archiving") public boolean isUnarchival(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR; field public static final int INVALID_ID = -1; // 0xffffffff @@ -13296,6 +13298,7 @@ package android.content.pm { method @NonNull public java.util.List<android.content.pm.VersionedPackage> getDependentPackages(); method @IntRange(from=0xffffffff) public long getLongVersion(); method public String getName(); + method @FlaggedApi("android.content.pm.sdk_lib_independence") @NonNull public java.util.List<android.content.pm.VersionedPackage> getOptionalDependentPackages(); method public int getType(); method @Deprecated @IntRange(from=0xffffffff) public int getVersion(); method public void writeToParcel(android.os.Parcel, int); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index e7803fbf011d..d395b8cf936e 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -136,6 +136,7 @@ package android.content { package android.content.pm { public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + method @FlaggedApi("android.content.pm.sdk_lib_independence") @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getOptionalSharedLibraryInfos(); method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraryInfos(); field public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1; // 0xffffffff field public static final int HIDDEN_API_ENFORCEMENT_DISABLED = 0; // 0x0 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 225b31ce37ed..802354e08653 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9878,8 +9878,8 @@ package android.nfc.cardemulation { method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getSubsetAids(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean hasCategory(@NonNull String); + method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isCategoryOtherServiceEnabled(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOnHost(); - method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOtherServiceEnabled(); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager); @@ -9888,9 +9888,9 @@ package android.nfc.cardemulation { method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresScreenOn(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresUnlock(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement(); + method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setCategoryOtherServiceEnabled(boolean); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String); - method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOtherServiceEnabled(boolean); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int); field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR; } @@ -12930,6 +12930,7 @@ package android.service.voice { field public static final int CONFIDENCE_LEVEL_LOW = 1; // 0x1 field public static final int CONFIDENCE_LEVEL_MEDIUM = 2; // 0x2 field public static final int CONFIDENCE_LEVEL_NONE = 0; // 0x0 + field @FlaggedApi("android.service.voice.flags.allow_hotword_bump_egress") public static final int CONFIDENCE_LEVEL_VERY_HIGH = 4; // 0x4 field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordRejectedResult> CREATOR; } @@ -13701,6 +13702,7 @@ package android.telephony { method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>); method public int describeContents(); method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_restriction_status") public int getCarrierRestrictionStatus(); method public int getDefaultCarrierRestriction(); method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers(); method public int getMultiSimPolicy(); @@ -14523,7 +14525,6 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void clearRadioPowerOffForReason(int); method public void dial(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean disableDataConnectivity(); - method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableCellularIdentifierDisclosureNotifications(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableModemForSlot(int, boolean); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean); @@ -14596,7 +14597,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int); - method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCellularIdentifierDisclosureNotificationEnabled(); + method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCellularIdentifierDisclosureNotificationsEnabled(); method public boolean isDataConnectivityPossible(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled(); @@ -14644,6 +14645,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean); + method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableCellularIdentifierDisclosureNotifications(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean); diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index dec1ee52712d..cef11bb42c3f 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -1909,6 +1909,8 @@ SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilit SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addAccessibilityStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.view.inputmethod.InputMethodInfo#dump(android.util.Printer, String): + SAM-compatible parameters (such as parameter 1, "pw", in android.view.inputmethod.InputMethodInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams): SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4a9fa9e63bf9..5674a108baaa 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3061,10 +3061,10 @@ public class Activity extends ContextThemeWrapper } /** - * Request to put the a freeform activity into fullscreen. This will only be allowed if the - * activity is on a freeform display, such as a desktop device. The requester has to be the - * top-most activity of the focused display, and the request should be a response to a user - * input. When getting fullscreen and receiving corresponding + * Request to put the freeform activity into fullscreen. The requester has to be the top-most + * activity of the focused display which can be verified using + * {@link #onTopResumedActivityChanged(boolean)}. The request should also be a response to a + * user input. When getting fullscreen and receiving corresponding * {@link #onConfigurationChanged(Configuration)} and * {@link #onMultiWindowModeChanged(boolean, Configuration)}, the activity should relayout * itself and the system bars' visibilities can be controlled as usual fullscreen apps. @@ -3072,11 +3072,6 @@ public class Activity extends ContextThemeWrapper * Calling it again with the exit request can restore the activity to the previous status. * This will only happen when it got into fullscreen through this API. * - * If an app wants to be in fullscreen always, it should claim as not being resizable - * by setting - * <a href="https://developer.android.com/guide/topics/large-screens/multi-window-support#resizeableActivity"> - * {@code android:resizableActivity="false"}</a> instead of calling this API. - * * @param request Can be {@link #FULLSCREEN_MODE_REQUEST_ENTER} or * {@link #FULLSCREEN_MODE_REQUEST_EXIT} to indicate this request is to get * fullscreen or get restored. diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 87c86df6140d..287d2bd9e6a7 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2909,7 +2909,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras, launcherExtras, dialogInfo, flags, mContext.getOpPackageName(), - getUserId()); + UserHandle.myUserId() /* suspendingUserId */, getUserId() /* targetUserId */); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index 53a21cdd78c9..343348b89625 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -645,8 +645,8 @@ public final class AutomaticZenRule implements Parcelable { } public Builder(@NonNull String name, @NonNull Uri conditionId) { - mName = name; - mConditionId = conditionId; + mName = Objects.requireNonNull(name); + mConditionId = Objects.requireNonNull(conditionId); } /** diff --git a/core/java/android/app/FullscreenRequestHandler.java b/core/java/android/app/FullscreenRequestHandler.java index 52f461daf233..c78c66aa62c0 100644 --- a/core/java/android/app/FullscreenRequestHandler.java +++ b/core/java/android/app/FullscreenRequestHandler.java @@ -16,8 +16,7 @@ package android.app; -import static android.app.Activity.FULLSCREEN_MODE_REQUEST_ENTER; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.Activity.FULLSCREEN_MODE_REQUEST_EXIT; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import android.annotation.IntDef; @@ -35,18 +34,14 @@ import android.os.OutcomeReceiver; public class FullscreenRequestHandler { @IntDef(prefix = { "RESULT_" }, value = { RESULT_APPROVED, - RESULT_FAILED_NOT_IN_FREEFORM, RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY, - RESULT_FAILED_NOT_DEFAULT_FREEFORM, RESULT_FAILED_NOT_TOP_FOCUSED }) public @interface RequestResult {} public static final int RESULT_APPROVED = 0; - public static final int RESULT_FAILED_NOT_IN_FREEFORM = 1; - public static final int RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY = 2; - public static final int RESULT_FAILED_NOT_DEFAULT_FREEFORM = 3; - public static final int RESULT_FAILED_NOT_TOP_FOCUSED = 4; + public static final int RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY = 1; + public static final int RESULT_FAILED_NOT_TOP_FOCUSED = 2; public static final String REMOTE_CALLBACK_RESULT_KEY = "result"; @@ -85,17 +80,10 @@ public class FullscreenRequestHandler { OutcomeReceiver<Void, Throwable> callback, int result) { Throwable e = null; switch (result) { - case RESULT_FAILED_NOT_IN_FREEFORM: - e = new IllegalStateException("The window is not a freeform window, the request " - + "to get into fullscreen cannot be approved."); - break; case RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY: e = new IllegalStateException("The window is not in fullscreen by calling the " + "requestFullscreenMode API before, such that cannot be restored."); break; - case RESULT_FAILED_NOT_DEFAULT_FREEFORM: - e = new IllegalStateException("The window is not launched in freeform by default."); - break; case RESULT_FAILED_NOT_TOP_FOCUSED: e = new IllegalStateException("The window is not the top focused window."); break; @@ -109,11 +97,7 @@ public class FullscreenRequestHandler { } private static int earlyCheckRequestMatchesWindowingMode(int request, int windowingMode) { - if (request == FULLSCREEN_MODE_REQUEST_ENTER) { - if (windowingMode != WINDOWING_MODE_FREEFORM) { - return RESULT_FAILED_NOT_IN_FREEFORM; - } - } else { + if (request == FULLSCREEN_MODE_REQUEST_EXIT) { if (windowingMode != WINDOWING_MODE_FULLSCREEN) { return RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY; } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 16a80e93326a..3713380485ea 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -19,6 +19,7 @@ package android.content.pm; import static android.os.Build.VERSION_CODES.DONUT; import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1065,11 +1066,25 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving * the structure. * + * NOTE: the list also contains the result of {@link #getOptionalSharedLibraryInfos}. + * * {@hide} */ + @Nullable public List<SharedLibraryInfo> sharedLibraryInfos; /** + * List of all shared libraries this application is optionally linked against. + * This field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES + * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving + * the structure. + * + * @hide + */ + @Nullable + public List<SharedLibraryInfo> optionalSharedLibraryInfos; + + /** * Full path to the default directory assigned to the package for its * persistent data. */ @@ -1937,6 +1952,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { seInfoUser = orig.seInfoUser; sharedLibraryFiles = orig.sharedLibraryFiles; sharedLibraryInfos = orig.sharedLibraryInfos; + optionalSharedLibraryInfos = orig.optionalSharedLibraryInfos; dataDir = orig.dataDir; deviceProtectedDataDir = orig.deviceProtectedDataDir; credentialProtectedDataDir = orig.credentialProtectedDataDir; @@ -2029,6 +2045,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString8(seInfoUser); dest.writeString8Array(sharedLibraryFiles); dest.writeTypedList(sharedLibraryInfos); + dest.writeTypedList(optionalSharedLibraryInfos); dest.writeString8(dataDir); dest.writeString8(deviceProtectedDataDir); dest.writeString8(credentialProtectedDataDir); @@ -2129,6 +2146,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { seInfoUser = source.readString8(); sharedLibraryFiles = source.createString8Array(); sharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR); + optionalSharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR); dataDir = source.readString8(); deviceProtectedDataDir = source.readString8(); credentialProtectedDataDir = source.readString8(); @@ -2760,6 +2778,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure. * + * NOTE: the list also contains the result of {@link #getOptionalSharedLibraryInfos}. + * * @hide */ @NonNull @@ -2772,6 +2792,23 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } /** + * List of all shared libraries this application is optionally linked against. This + * list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES + * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure. + * + * @hide + */ + @NonNull + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @FlaggedApi(Flags.FLAG_SDK_LIB_INDEPENDENCE) + public List<SharedLibraryInfo> getOptionalSharedLibraryInfos() { + if (optionalSharedLibraryInfos == null) { + return Collections.EMPTY_LIST; + } + return optionalSharedLibraryInfos; + } + + /** * Gets the trusted host certificate digests of apps that are allowed to embed activities of * this application. The digests are computed using the SHA-256 digest algorithm. * @see android.R.attr#knownActivityEmbeddingCerts diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index 6f7299aa8e31..a97de6368b8c 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -65,6 +65,8 @@ interface ILauncherApps { in UserHandle user); LauncherUserInfo getLauncherUserInfo(in UserHandle user); List<String> getPreInstalledSystemPackages(in UserHandle user); + IntentSender getAppMarketActivityIntent(String callingPackage, String packageName, + in UserHandle user); void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage, String callingFeatureId, in ComponentName component, in Rect sourceBounds, in Bundle opts, in UserHandle user); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 98623de810c4..6dc8d4738c87 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -300,7 +300,8 @@ interface IPackageManager { String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, in PersistableBundle appExtras, in PersistableBundle launcherExtras, - in SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId); + in SuspendDialogInfo dialogInfo, int flags, String suspendingPackage, + int suspendingUserId, int targetUserId); String[] getUnsuspendablePackagesForUser(in String[] packageNames, int userId); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index ccc8f0956865..1d2b1aff46bc 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -799,6 +799,55 @@ public class LauncherApps { } } + + /** + * Returns an intent sender which can be used to start the App Market activity (Installer + * Activity). + * This method is primarily used to get an intent sender which starts App Market activity for + * another profile, if the caller is not otherwise allowed to start activity in that profile. + * + * <p>When packageName is set, intent sender to start the App Market Activity which installed + * the package in calling user will be returned, but for the profile passed. + * + * <p>When packageName is not set, intent sender to launch the default App Market Activity for + * the profile will be returned. In case there are multiple App Market Activities available for + * the profile, IntentPicker will be started, allowing user to choose the preferred activity. + * + * <p>The method will fall back to the behaviour of not having the packageName set, in case: + * <ul> + * <li>No activity for the packageName is found in calling user-space.</li> + * <li>The App Market Activity which installed the package in calling user-space is not + * present.</li> + * <li>The App Market Activity which installed the package in calling user-space is not + * present in the profile passed.</li> + * </ul> + * </p> + * + * + * + * @param packageName the package for which intent sender to launch App Market Activity is + * required. + * @param user the profile for which intent sender to launch App Market Activity is required. + * @return {@link IntentSender} object which launches the App Market Activity, null in case + * there is no such activity. + * @hide + */ + @Nullable + @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) + public IntentSender getAppMarketActivityIntent(@Nullable String packageName, + @NonNull UserHandle user) { + if (DEBUG) { + Log.i(TAG, "getAppMarketActivityIntent for package: " + packageName + + " user: " + user); + } + try { + return mService.getAppMarketActivityIntent(mContext.getPackageName(), + packageName, user); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + /** * Returns the list of the system packages that are installed at user creation. * diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index e395127dfaf3..0e131b413d0c 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -4381,6 +4381,17 @@ public class PackageInstaller { return pendingUserActionReason; } + /** + * Returns true if the session is an unarchival. + * + * @see PackageInstaller#requestUnarchive + */ + @FlaggedApi(Flags.FLAG_ARCHIVING) + public boolean isUnarchival() { + return (installFlags & PackageManager.INSTALL_UNARCHIVE) != 0; + } + + @Override public int describeContents() { return 0; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a5d16f2f6be1..a8638708824b 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1517,6 +1517,7 @@ public abstract class PackageManager { INSTALL_REQUEST_UPDATE_OWNERSHIP, INSTALL_IGNORE_DEXOPT_PROFILE, INSTALL_UNARCHIVE_DRAFT, + INSTALL_UNARCHIVE, }) @Retention(RetentionPolicy.SOURCE) public @interface InstallFlags {} @@ -1764,13 +1765,22 @@ public abstract class PackageManager { * If set, then the session is a draft session created for an upcoming unarchival by its * installer. * - * @see PackageInstaller#requestUnarchive(String) + * @see PackageInstaller#requestUnarchive * * @hide */ public static final int INSTALL_UNARCHIVE_DRAFT = 1 << 29; /** + * If set, then the {@link PackageInstaller.Session} is an unarchival. + * + * @see PackageInstaller#requestUnarchive + * + * @hide + */ + public static final int INSTALL_UNARCHIVE = 1 << 30; + + /** * Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is * a development-only feature and should not be used on end user devices. * @@ -9990,6 +10000,9 @@ public abstract class PackageManager { * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or * {@link android.Manifest.permission#SUSPEND_APPS}. * + * <p> + * <strong>Note:</strong>This API doesn't support cross user suspension and should only be used + * for testing. * @param suspendedPackage The package that has been suspended. * @return Name of the package that suspended the given package. Returns {@code null} if the * given package is not currently suspended and the platform package name - i.e. diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index 25ba72551b04..5acebf54a159 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -23,6 +24,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; +import android.util.Pair; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -99,26 +101,61 @@ public final class SharedLibraryInfo implements Parcelable { private final boolean mIsNative; private final VersionedPackage mDeclaringPackage; private final List<VersionedPackage> mDependentPackages; + + private final List<VersionedPackage> mOptionalDependentPackages; private List<SharedLibraryInfo> mDependencies; /** * Creates a new instance. * + * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of + * jars of + * this shared library. Null for builtin library. + * @param name The lib name. + * @param version The lib version if not builtin. + * @param type The lib type. + * @param declaringPackage The package that declares the library. + * @param dependentPackages The packages that depend on the library. + * @param isNative indicate if this shared lib is a native lib or not (i.e. java) + * @hide + */ + public SharedLibraryInfo(String path, String packageName, List<String> codePaths, + String name, long version, int type, + VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages, + List<SharedLibraryInfo> dependencies, boolean isNative) { + mPath = path; + mPackageName = packageName; + mCodePaths = codePaths; + mName = name; + mVersion = version; + mType = type; + mDeclaringPackage = declaringPackage; + mDependentPackages = dependentPackages; + mDependencies = dependencies; + mIsNative = isNative; + mOptionalDependentPackages = null; + } + + /** + * Creates a new instance. + * * @param codePaths For a non {@link #TYPE_BUILTIN builtin} library, the locations of jars of * this shared library. Null for builtin library. * @param name The lib name. * @param version The lib version if not builtin. * @param type The lib type. * @param declaringPackage The package that declares the library. - * @param dependentPackages The packages that depend on the library. * @param isNative indicate if this shared lib is a native lib or not (i.e. java) + * @param allDependentPackages All packages that depend on the library (including the optional + * sdk libraries). * * @hide */ public SharedLibraryInfo(String path, String packageName, List<String> codePaths, String name, long version, int type, - VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages, - List<SharedLibraryInfo> dependencies, boolean isNative) { + VersionedPackage declaringPackage, + List<SharedLibraryInfo> dependencies, boolean isNative, + Pair<List<VersionedPackage>, List<Boolean>> allDependentPackages) { mPath = path; mPackageName = packageName; mCodePaths = codePaths; @@ -126,9 +163,28 @@ public final class SharedLibraryInfo implements Parcelable { mVersion = version; mType = type; mDeclaringPackage = declaringPackage; - mDependentPackages = dependentPackages; mDependencies = dependencies; mIsNative = isNative; + + var allDependents = allDependentPackages.first; + var usesLibOptional = allDependentPackages.second; + mDependentPackages = allDependents; + List<VersionedPackage> optionalDependents = null; + if (mType == SharedLibraryInfo.TYPE_SDK_PACKAGE + && Flags.sdkLibIndependence() && allDependents != null + && usesLibOptional != null + && allDependents.size() == usesLibOptional.size()) { + for (int k = 0; k < allDependents.size(); k++) { + VersionedPackage versionedPackage = allDependents.get(k); + if (usesLibOptional.get(k)) { + if (optionalDependents == null) { + optionalDependents = new ArrayList<>(); + } + optionalDependents.add(versionedPackage); + } + } + } + mOptionalDependentPackages = optionalDependents; } private SharedLibraryInfo(Parcel parcel) { @@ -148,6 +204,8 @@ public final class SharedLibraryInfo implements Parcelable { parcel.readArrayList(null, android.content.pm.VersionedPackage.class); mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR); mIsNative = parcel.readBoolean(); + mOptionalDependentPackages = parcel.readParcelableList(new ArrayList<>(), + VersionedPackage.class.getClassLoader(), VersionedPackage.class); } /** @@ -324,6 +382,8 @@ public final class SharedLibraryInfo implements Parcelable { /** * Gets the packages that depend on the library. * + * NOTE: the list also contains the result of {@link #getOptionalDependentPackages}. + * * @return The dependent packages. */ public @NonNull List<VersionedPackage> getDependentPackages() { @@ -333,6 +393,19 @@ public final class SharedLibraryInfo implements Parcelable { return mDependentPackages; } + /** + * Gets the packages that optionally depend on the library. + * + * @return The dependent packages. + */ + @FlaggedApi(Flags.FLAG_SDK_LIB_INDEPENDENCE) + public @NonNull List<VersionedPackage> getOptionalDependentPackages() { + if (mOptionalDependentPackages == null) { + return Collections.emptyList(); + } + return mOptionalDependentPackages; + } + @Override public int describeContents() { return 0; @@ -362,6 +435,7 @@ public final class SharedLibraryInfo implements Parcelable { parcel.writeList(mDependentPackages); parcel.writeTypedList(mDependencies); parcel.writeBoolean(mIsNative); + parcel.writeParcelableList(mOptionalDependentPackages, flags); } private static String typeToString(int type) { diff --git a/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl b/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl new file mode 100644 index 000000000000..73ac333cfd89 --- /dev/null +++ b/core/java/android/hardware/biometrics/AuthenticationStateListener.aidl @@ -0,0 +1,36 @@ +/* + * 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 android.hardware.biometrics; + +/** + * Low-level callback interface between <Biometric>Manager and <Auth>Service. Allows core system + * services (e.g. SystemUI) to register a listener for updates about the current state of biometric + * authentication. + * @hide + */ +oneway interface AuthenticationStateListener { + /** + * Defines behavior in response to authentication starting + * @param requestReason reason from [BiometricRequestConstants.RequestReason] for requesting + * authentication starting + */ + void onAuthenticationStarted(int requestReason); + + /** + * Defines behavior in response to authentication stopping + */ + void onAuthenticationStopped(); +} diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index f82f79eba8c9..d7d1d1a7c677 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -552,6 +552,44 @@ public class BiometricManager { } /** + * Registers listener for changes to biometric authentication state. + * Only sends callbacks for events that occur after the callback has been registered. + * @param listener Listener for changes to biometric authentication state + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void registerAuthenticationStateListener(AuthenticationStateListener listener) { + if (mService != null) { + try { + mService.registerAuthenticationStateListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "registerAuthenticationStateListener(): Service not connected"); + } + } + + /** + * Unregisters listener for changes to biometric authentication state. + * @param listener Listener for changes to biometric authentication state + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void unregisterAuthenticationStateListener(AuthenticationStateListener listener) { + if (mService != null) { + try { + mService.unregisterAuthenticationStateListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "unregisterAuthenticationStateListener(): Service not connected"); + } + } + + + /** * Requests all {@link Authenticators.Types#BIOMETRIC_STRONG} sensors to have their * authenticatorId invalidated for the specified user. This happens when enrollments have been * added on devices with multiple biometric sensors. diff --git a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java b/core/java/android/hardware/biometrics/BiometricRequestConstants.java index 065ae64a92ad..b036f309f7df 100644 --- a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java +++ b/core/java/android/hardware/biometrics/BiometricRequestConstants.java @@ -22,24 +22,27 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Common constants for biometric overlays. + * Common constants for biometric requests. * @hide */ -public interface BiometricOverlayConstants { +public class BiometricRequestConstants { + + private BiometricRequestConstants() {} + /** Unknown usage. */ - int REASON_UNKNOWN = 0; + public static final int REASON_UNKNOWN = 0; /** User is about to enroll. */ - int REASON_ENROLL_FIND_SENSOR = 1; + public static final int REASON_ENROLL_FIND_SENSOR = 1; /** User is enrolling. */ - int REASON_ENROLL_ENROLLING = 2; + public static final int REASON_ENROLL_ENROLLING = 2; /** Usage from BiometricPrompt. */ - int REASON_AUTH_BP = 3; - /** Usage from Keyguard. */ - int REASON_AUTH_KEYGUARD = 4; + public static final int REASON_AUTH_BP = 3; + /** Usage from Device Entry. */ + public static final int REASON_AUTH_KEYGUARD = 4; /** Non-specific usage (from FingerprintManager). */ - int REASON_AUTH_OTHER = 5; + public static final int REASON_AUTH_OTHER = 5; /** Usage from Settings. */ - int REASON_AUTH_SETTINGS = 6; + public static final int REASON_AUTH_SETTINGS = 6; @IntDef({REASON_UNKNOWN, REASON_ENROLL_FIND_SENSOR, @@ -49,5 +52,5 @@ public interface BiometricOverlayConstants { REASON_AUTH_OTHER, REASON_AUTH_SETTINGS}) @Retention(RetentionPolicy.SOURCE) - @interface ShowReason {} + public @interface RequestReason {} } diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl index 5bdbe2b573b2..8514f98fbf0d 100644 --- a/core/java/android/hardware/biometrics/IAuthService.aidl +++ b/core/java/android/hardware/biometrics/IAuthService.aidl @@ -16,6 +16,7 @@ package android.hardware.biometrics; +import android.hardware.biometrics.AuthenticationStateListener; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IInvalidationCallback; @@ -66,6 +67,12 @@ interface IAuthService { // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); + // Register listener for changes to authentication state. + void registerAuthenticationStateListener(AuthenticationStateListener listener); + + // Unregister listener for changes to authentication state. + void unregisterAuthenticationStateListener(AuthenticationStateListener listener); + // Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the // specified user. This happens when enrollments have been added on devices with multiple // biometric sensors. diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 935157a48a45..fe7de8333784 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -983,6 +983,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR /** * @hide */ diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 0100660669e9..f594c00b0e47 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -15,6 +15,7 @@ */ package android.hardware.fingerprint; +import android.hardware.biometrics.AuthenticationStateListener; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.biometrics.IBiometricStateListener; @@ -203,6 +204,14 @@ interface IFingerprintService { @EnforcePermission("USE_BIOMETRIC_INTERNAL") void setSidefpsController(in ISidefpsController controller); + // Registers AuthenticationStateListener. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") + void registerAuthenticationStateListener(AuthenticationStateListener listener); + + // Unregisters AuthenticationStateListener. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") + void unregisterAuthenticationStateListener(AuthenticationStateListener listener); + // Registers BiometricStateListener. @EnforcePermission("USE_BIOMETRIC_INTERNAL") void registerBiometricStateListener(IBiometricStateListener listener); diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 98a980f5e7f8..f407fb73534f 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -284,6 +284,20 @@ public final class NfcAdapter { public static final int STATE_TURNING_OFF = 4; /** + * Possible states from {@link #getAdapterState}. + * + * @hide + */ + @IntDef(prefix = { "STATE_" }, value = { + STATE_OFF, + STATE_TURNING_ON, + STATE_ON, + STATE_TURNING_OFF + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AdapterState{} + + /** * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. * <p> * Setting this flag enables polling for Nfc-A technology. @@ -948,7 +962,7 @@ public final class NfcAdapter { */ @SystemApi @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public int getAdapterState() { + public @AdapterState int getAdapterState() { try { return sService.getState(); } catch (RemoteException e) { diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index e331c95288d9..bd087f970426 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -130,7 +130,7 @@ public final class ApduServiceInfo implements Parcelable { /** * State of the service for CATEGORY_OTHER selection */ - private boolean mOtherServiceEnabled; + private boolean mCategoryOtherServiceEnabled; /** * @hide @@ -183,7 +183,7 @@ public final class ApduServiceInfo implements Parcelable { this.mBannerResourceId = bannerResource; this.mUid = uid; this.mSettingsActivityName = settingsActivityName; - this.mOtherServiceEnabled = isEnabled; + this.mCategoryOtherServiceEnabled = isEnabled; } @@ -374,7 +374,7 @@ public final class ApduServiceInfo implements Parcelable { // Set uid mUid = si.applicationInfo.uid; - mOtherServiceEnabled = false; // support other category + mCategoryOtherServiceEnabled = false; // support other category } @@ -746,7 +746,7 @@ public final class ApduServiceInfo implements Parcelable { dest.writeInt(mUid); dest.writeString(mSettingsActivityName); - dest.writeInt(mOtherServiceEnabled ? 1 : 0); + dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0); }; @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @@ -809,7 +809,7 @@ public final class ApduServiceInfo implements Parcelable { pw.println(" Static AID groups:"); for (AidGroup group : mStaticAidGroups.values()) { pw.println(" Category: " + group.getCategory() - + "(enabled: " + mOtherServiceEnabled + ")"); + + "(enabled: " + mCategoryOtherServiceEnabled + ")"); for (String aid : group.getAids()) { pw.println(" AID: " + aid); } @@ -817,7 +817,7 @@ public final class ApduServiceInfo implements Parcelable { pw.println(" Dynamic AID groups:"); for (AidGroup group : mDynamicAidGroups.values()) { pw.println(" Category: " + group.getCategory() - + "(enabled: " + mOtherServiceEnabled + ")"); + + "(enabled: " + mCategoryOtherServiceEnabled + ")"); for (String aid : group.getAids()) { pw.println(" AID: " + aid); } @@ -834,8 +834,8 @@ public final class ApduServiceInfo implements Parcelable { * @param enabled true to indicate if user has enabled this service */ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public void setOtherServiceEnabled(boolean enabled) { - mOtherServiceEnabled = enabled; + public void setCategoryOtherServiceEnabled(boolean enabled) { + mCategoryOtherServiceEnabled = enabled; } @@ -845,8 +845,8 @@ public final class ApduServiceInfo implements Parcelable { * @return true to indicate if user has enabled this service */ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) - public boolean isOtherServiceEnabled() { - return mOtherServiceEnabled; + public boolean isCategoryOtherServiceEnabled() { + return mCategoryOtherServiceEnabled; } /** diff --git a/core/java/android/service/voice/HotwordRejectedResult.java b/core/java/android/service/voice/HotwordRejectedResult.java index 26c1ca428c3b..eb1ac67719ed 100644 --- a/core/java/android/service/voice/HotwordRejectedResult.java +++ b/core/java/android/service/voice/HotwordRejectedResult.java @@ -16,9 +16,11 @@ package android.service.voice; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.SystemApi; import android.os.Parcelable; +import android.service.voice.flags.Flags; import com.android.internal.util.DataClass; @@ -53,12 +55,17 @@ public final class HotwordRejectedResult implements Parcelable { /** High confidence in hotword detector result. */ public static final int CONFIDENCE_LEVEL_HIGH = 3; + /** Very high confidence in hotword detector result. **/ + @FlaggedApi(Flags.FLAG_ALLOW_HOTWORD_BUMP_EGRESS) + public static final int CONFIDENCE_LEVEL_VERY_HIGH = 4; + /** @hide */ @IntDef(prefix = {"CONFIDENCE_LEVEL_"}, value = { CONFIDENCE_LEVEL_NONE, CONFIDENCE_LEVEL_LOW, CONFIDENCE_LEVEL_MEDIUM, - CONFIDENCE_LEVEL_HIGH + CONFIDENCE_LEVEL_HIGH, + CONFIDENCE_LEVEL_VERY_HIGH }) @Retention(RetentionPolicy.SOURCE) @interface HotwordConfidenceLevelValue { @@ -91,9 +98,10 @@ public final class HotwordRejectedResult implements Parcelable { CONFIDENCE_LEVEL_NONE, CONFIDENCE_LEVEL_LOW, CONFIDENCE_LEVEL_MEDIUM, - CONFIDENCE_LEVEL_HIGH + CONFIDENCE_LEVEL_HIGH, + CONFIDENCE_LEVEL_VERY_HIGH }) - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member public @interface ConfidenceLevel {} @@ -109,6 +117,8 @@ public final class HotwordRejectedResult implements Parcelable { return "CONFIDENCE_LEVEL_MEDIUM"; case CONFIDENCE_LEVEL_HIGH: return "CONFIDENCE_LEVEL_HIGH"; + case CONFIDENCE_LEVEL_VERY_HIGH: + return "CONFIDENCE_LEVEL_VERY_HIGH"; default: return Integer.toHexString(value); } } @@ -259,10 +269,10 @@ public final class HotwordRejectedResult implements Parcelable { } @DataClass.Generated( - time = 1621961370106L, + time = 1701990933632L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/voice/HotwordRejectedResult.java", - inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_HIGH\nprivate final @android.service.voice.HotwordRejectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate static int defaultConfidenceLevel()\nclass HotwordRejectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") + inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final @android.annotation.FlaggedApi int CONFIDENCE_LEVEL_VERY_HIGH\nprivate final @android.service.voice.HotwordRejectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate static int defaultConfidenceLevel()\nclass HotwordRejectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig index c414ef8a6826..b596666bf607 100644 --- a/core/java/android/service/voice/flags/flags.aconfig +++ b/core/java/android/service/voice/flags/flags.aconfig @@ -6,3 +6,10 @@ flag { description: "This flag allows the hotword detection service to egress training data to the default assistant." bug: "296074924" } + +flag { + name: "allow_hotword_bump_egress" + namespace: "machine_learning" + description: "This flag allows hotword detection service to egress reason code for hotword bump." + bug: "290951024" +} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c7e180732fd4..e3d9c605ff63 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -592,6 +592,13 @@ public interface WindowManager extends ViewManager { int TRANSIT_FLAG_KEYGUARD_UNOCCLUDING = (1 << 13); // 0x2000 /** + * Transition flag: Indicates that there is a physical display switch + * TODO(b/316112906) remove after defer_display_updates flag roll out + * @hide + */ + int TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH = (1 << 14); // 0x4000 + + /** * @hide */ @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = { @@ -608,7 +615,8 @@ public interface WindowManager extends ViewManager { TRANSIT_FLAG_INVISIBLE, TRANSIT_FLAG_KEYGUARD_APPEARING, TRANSIT_FLAG_KEYGUARD_OCCLUDING, - TRANSIT_FLAG_KEYGUARD_UNOCCLUDING + TRANSIT_FLAG_KEYGUARD_UNOCCLUDING, + TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH, }) @Retention(RetentionPolicy.SOURCE) @interface TransitionFlags {} diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index efc1455ecd45..f1aa330f562c 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -39,6 +39,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.SuspendDialogInfo; +import android.content.pm.UserPackage; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -308,7 +309,8 @@ public class SuspendedAppActivity extends AlertActivity try { final String[] errored = ipm.setPackagesSuspendedAsUser( new String[]{mSuspendedPackage}, false, null, null, null, 0, - mSuspendingPackage, mUserId); + mSuspendingPackage, mUserId /* suspendingUserId */, + mUserId /* targetUserId */); if (ArrayUtils.contains(errored, mSuspendedPackage)) { Slog.e(TAG, "Could not unsuspend " + mSuspendedPackage); break; @@ -350,17 +352,18 @@ public class SuspendedAppActivity extends AlertActivity } public static Intent createSuspendedAppInterceptIntent(String suspendedPackage, - String suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options, + UserPackage suspendingPackage, SuspendDialogInfo dialogInfo, Bundle options, IntentSender onUnsuspend, int userId) { - return new Intent() + Intent intent = new Intent() .setClassName("android", SuspendedAppActivity.class.getName()) .putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage) .putExtra(EXTRA_DIALOG_INFO, dialogInfo) - .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage) + .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage.packageName) .putExtra(EXTRA_UNSUSPEND_INTENT, onUnsuspend) .putExtra(EXTRA_ACTIVITY_OPTIONS, options) .putExtra(Intent.EXTRA_USER_ID, userId) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + return intent; } } diff --git a/core/java/com/android/internal/net/TEST_MAPPING b/core/java/com/android/internal/net/TEST_MAPPING index 971ad36eecba..f935946c95c7 100644 --- a/core/java/com/android/internal/net/TEST_MAPPING +++ b/core/java/com/android/internal/net/TEST_MAPPING @@ -1,5 +1,5 @@ { - "postsubmit": [ + "presubmit": [ { "name": "FrameworksNetTests", "options": [ diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java index f7e1f7293ac6..83acc47d637f 100644 --- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java +++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java @@ -726,26 +726,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, this.usesSdkLibrariesVersionsMajor, versionMajor, true); this.usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class, this.usesSdkLibrariesCertDigests, certSha256Digests, true); - this.usesSdkLibrariesOptional = appendBoolean(this.usesSdkLibrariesOptional, + this.usesSdkLibrariesOptional = ArrayUtils.appendBoolean(this.usesSdkLibrariesOptional, usesSdkLibrariesOptional); return this; } - /** - * Adds value to given array if not already present, providing set-like - * behavior. - */ - public static boolean[] appendBoolean(@Nullable boolean[] cur, boolean val) { - if (cur == null) { - return new boolean[] { val }; - } - final int N = cur.length; - boolean[] ret = new boolean[N + 1]; - System.arraycopy(cur, 0, ret, 0, N); - ret[N] = val; - return ret; - } - @Override public PackageImpl addUsesStaticLibrary(String libraryName, long version, String[] certSha256Digests) { diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index 9d0be4bf8ee6..8f00f79e7179 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -623,6 +623,21 @@ public class ArrayUtils { * Adds value to given array if not already present, providing set-like * behavior. */ + public static boolean[] appendBoolean(@Nullable boolean[] cur, boolean val) { + if (cur == null) { + return new boolean[] { val }; + } + final int N = cur.length; + boolean[] ret = new boolean[N + 1]; + System.arraycopy(cur, 0, ret, 0, N); + ret[N] = val; + return ret; + } + + /** + * Adds value to given array if not already present, providing set-like + * behavior. + */ public static @NonNull long[] appendLong(@Nullable long[] cur, long val) { return appendLong(cur, val, false); } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 91dfc6023e42..55100a5347fd 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -282,6 +282,11 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin void android_os_Process_setProcessFrozen( JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze) { + if (uid < 0) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "uid is negative: %d", uid); + return; + } + bool success = true; if (freeze) { @@ -305,6 +310,11 @@ jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid) } jint android_os_Process_createProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid) { + if (uid < 0) { + return jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "uid is negative: %d", uid); + } + return createProcessGroup(uid, pid); } @@ -590,12 +600,21 @@ void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name) jint android_os_Process_setUid(JNIEnv* env, jobject clazz, jint uid) { + if (uid < 0) { + return jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "uid is negative: %d", uid); + } + return setuid(uid) == 0 ? 0 : errno; } -jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid) -{ - return setgid(uid) == 0 ? 0 : errno; +jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint gid) { + if (gid < 0) { + return jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "gid is negative: %d", gid); + } + + return setgid(gid) == 0 ? 0 : errno; } static int pid_compare(const void* v1, const void* v2) @@ -1235,11 +1254,21 @@ jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz, jint android_os_Process_killProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid) { + if (uid < 0) { + return jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "uid is negative: %d", uid); + } + return killProcessGroup(uid, pid, SIGKILL); } jint android_os_Process_sendSignalToProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid, jint signal) { + if (uid < 0) { + return jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "uid is negative: %d", uid); + } + return sendSignalToProcessGroup(uid, pid, signal); } @@ -1258,6 +1287,11 @@ static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, j } void android_os_Process_freezeCgroupUID(JNIEnv* env, jobject clazz, jint uid, jboolean freeze) { + if (uid < 0) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "uid is negative: %d", uid); + return; + } + bool success = true; if (freeze) { diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index e1c1a42eed81..5cfb1a3e44ba 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -9802,6 +9802,17 @@ <attr name="supportedTypes" format="reference" /> </declare-styleable> + <!-- Use <code>tv-ad-service</code> as the root tag of the XML resource that describes a + android.media.tv.ad.TvAdService, which is referenced from its + android.media.tv.ad.TvAdService#SERVICE_META_DATA meta-data entry. Described here + are the attributes that can be included in that tag. --> + <declare-styleable name="TvAdService"> + <!-- The advertisement types that the TV ad service supports. + Reference to a string array resource that describes the supported types, + e.g. linear, overlay. --> + <attr name="adServiceTypes" format="reference" /> + </declare-styleable> + <!-- Attributes that can be used with <code>rating-system-definition</code> tags inside of the XML resource that describes TV content rating of a {@link android.media.tv.TvInputService}, diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index bf8e55fd986f..d0de5f0e4104 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4255,6 +4255,9 @@ --> <bool name="config_wallpaperTopApp">false</bool> + <!-- True if the device supports dVRR --> + <bool name="config_supportsDvrr">false</bool> + <!-- True if the device supports at least one form of multi-window. E.g. freeform, split-screen, picture-in-picture. --> <bool name="config_supportsMultiWindow">true</bool> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index f10e7f8c337e..540967d28d9b 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -117,6 +117,8 @@ <public name="isVirtualDeviceOnly"/> <!-- @FlaggedApi("android.content.pm.sdk_lib_independence") --> <public name="optional"/> + <!-- @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") --> + <public name="adServiceTypes" /> </staging-public-group> <staging-public-group type="id" first-id="0x01bc0000"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fd6158d02b8f..ef272ee8bef1 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -404,6 +404,7 @@ <java-symbol type="bool" name="config_supportAudioSourceUnprocessed" /> <java-symbol type="bool" name="config_freeformWindowManagement" /> <java-symbol type="bool" name="config_supportsBubble" /> + <java-symbol type="bool" name="config_supportsDvrr" /> <java-symbol type="bool" name="config_supportsMultiWindow" /> <java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" /> <java-symbol type="bool" name="config_supportsMultiDisplay" /> diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java index d629f6a8c57c..1925588e8904 100644 --- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java +++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java @@ -166,6 +166,15 @@ public class AutomaticZenRuleTest { @Test @EnableFlags(Flags.FLAG_MODES_API) + public void builderConstructor_nullInputs_throws() { + assertThrows(NullPointerException.class, + () -> new AutomaticZenRule.Builder(null, Uri.parse("condition"))); + assertThrows(NullPointerException.class, + () -> new AutomaticZenRule.Builder("name", null)); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) public void validate_builderWithValidType_succeeds() throws Exception { AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")) .setType(AutomaticZenRule.TYPE_BEDTIME) diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java index 0c5e9664bbde..fc233fba082e 100644 --- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java @@ -161,6 +161,18 @@ public class ArrayUtilsTest { } @Test + public void testAppendBoolean() throws Exception { + assertArrayEquals(new boolean[] { true }, + ArrayUtils.appendBoolean(null, true)); + assertArrayEquals(new boolean[] { true }, + ArrayUtils.appendBoolean(new boolean[] { }, true)); + assertArrayEquals(new boolean[] { true, false }, + ArrayUtils.appendBoolean(new boolean[] { true }, false)); + assertArrayEquals(new boolean[] { true, true }, + ArrayUtils.appendBoolean(new boolean[] { true }, true)); + } + + @Test public void testRemoveLong() throws Exception { assertNull(ArrayUtils.removeLong(null, 1)); assertArrayEquals(new long[] { }, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java index c98090638010..8d8dc10951a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java @@ -49,6 +49,11 @@ public interface BackAnimation { @BackEvent.SwipeEdge int swipeEdge); /** + * Called when the input pointers are pilfered. + */ + void onPilferPointers(); + + /** * Sets whether the back gesture is past the trigger threshold or not. */ void setTriggerBack(boolean triggerBack); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index cf858dcb0637..d8c691b01b61 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -111,6 +111,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont /** Tracks if we should start the back gesture on the next motion move event */ private boolean mShouldStartOnNextMoveEvent = false; + private boolean mOnBackStartDispatched = false; private final FlingAnimationUtils mFlingAnimationUtils; @@ -304,6 +305,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } @Override + public void onPilferPointers() { + BackAnimationController.this.onPilferPointers(); + } + + @Override public void setTriggerBack(boolean triggerBack) { mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack)); } @@ -384,6 +390,16 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont return null; } + @VisibleForTesting + void onPilferPointers() { + mCurrentTracker.updateStartLocation(); + // Dispatch onBackStarted, only to app callbacks. + // System callbacks will receive onBackStarted when the remote animation starts. + if (!shouldDispatchToAnimator()) { + tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null)); + } + } + /** * Called when a new motion event needs to be transferred to this * {@link BackAnimationController} @@ -483,12 +499,15 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback(); // App is handling back animation. Cancel system animation latency tracking. cancelLatencyTracking(); - dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null)); + tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null)); } } private void onMove() { - if (!mBackGestureStarted || mBackNavigationInfo == null || mActiveCallback == null) { + if (!mBackGestureStarted + || mBackNavigationInfo == null + || mActiveCallback == null + || !mOnBackStartDispatched) { return; } // Skip dispatching if the move corresponds to the queued instead of the current gesture @@ -524,13 +543,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont && mBackNavigationInfo.isPrepareRemoteAnimation(); } - private void dispatchOnBackStarted(IOnBackInvokedCallback callback, + private void tryDispatchOnBackStarted(IOnBackInvokedCallback callback, BackMotionEvent backEvent) { - if (callback == null) { + if (callback == null || mOnBackStartDispatched) { return; } try { callback.onBackStarted(backEvent); + mOnBackStartDispatched = true; } catch (RemoteException e) { Log.e(TAG, "dispatchOnBackStarted error: ", e); } @@ -828,6 +848,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont void finishBackNavigation(boolean triggerBack) { ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()"); mActiveCallback = null; + mShouldStartOnNextMoveEvent = false; + mOnBackStartDispatched = false; mShellBackAnimationRegistry.resetDefaultCrossActivity(); cancelLatencyTracking(); if (mBackNavigationInfo != null) { @@ -909,7 +931,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ::onBackAnimationFinished)); if (apps.length >= 1) { - dispatchOnBackStarted( + mCurrentTracker.updateStartLocation(); + tryDispatchOnBackStarted( mActiveCallback, mCurrentTracker.createStartEvent(apps[0])); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java index 19eb928d4e30..4bd56d460818 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java @@ -104,6 +104,15 @@ class TouchTracker { mStartThresholdX = mInitTouchX; } + /** Update the start location used to compute the progress + * to the latest touch location. + */ + void updateStartLocation() { + mInitTouchX = mLatestTouchX; + mInitTouchY = mLatestTouchY; + mStartThresholdX = mInitTouchX; + } + void reset() { mInitTouchX = 0; mInitTouchY = 0; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index f5c01d063707..4c477373c32c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -769,7 +769,6 @@ public class PipAnimationController { getSurfaceTransactionHelper().crop(tx, leash, destBounds); } if (mContentOverlay != null) { - mContentOverlay.onAnimationEnd(tx, destBounds); clearContentOverlay(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java index a2bd47c5285e..e11e8596a7fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java @@ -67,15 +67,6 @@ public abstract class PipContentOverlay { public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx, Rect currentBounds, float fraction); - /** - * Callback when reaches the end of animation on the internal {@link #mLeash}. - * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly - * call apply on this transaction, it should be applied on the caller side. - * @param destinationBounds {@link Rect} of the final bounds. - */ - public abstract void onAnimationEnd(SurfaceControl.Transaction atomicTx, - Rect destinationBounds); - /** A {@link PipContentOverlay} uses solid color. */ public static final class PipColorOverlay extends PipContentOverlay { private static final String TAG = PipColorOverlay.class.getSimpleName(); @@ -107,11 +98,6 @@ public abstract class PipContentOverlay { atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2); } - @Override - public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) { - // Do nothing. Color overlay should be fully opaque by now, ready for fade out. - } - private float[] getContentOverlayColor(Context context) { final TypedArray ta = context.obtainStyledAttributes(new int[] { android.R.attr.colorBackground }); @@ -164,11 +150,6 @@ public abstract class PipContentOverlay { Rect currentBounds, float fraction) { // Do nothing. Keep the snapshot till animation ends. } - - @Override - public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) { - // Do nothing. Snapshot overlay should be fully opaque by now, ready for fade out. - } } /** A {@link PipContentOverlay} shows app icon on solid color background. */ @@ -255,11 +236,6 @@ public abstract class PipContentOverlay { } @Override - public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) { - // Do nothing. Icon overlay should be fully opaque by now, ready for fade out. - } - - @Override public void detach(SurfaceControl.Transaction tx) { super.detach(tx); if (mBitmap != null && !mBitmap.isRecycled()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 743b1ea197bc..3635165d76ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -329,15 +329,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private @Surface.Rotation int mCurrentRotation; /** - * An optional overlay used to mask content changing between an app in/out of PiP, only set if - * {@link PipTransitionState#getInSwipePipToHomeTransition()} is true, only in gesture nav. - */ - @Nullable - SurfaceControl mSwipePipToHomeOverlay; - - /** - * An optional overlay used to mask content changing between an app in/out of PiP, only set if - * {@link PipTransitionState#getInSwipePipToHomeTransition()} is false. + * An optional overlay used to mask content changing between an app in/out of PiP. */ @Nullable SurfaceControl mPipOverlay; @@ -480,7 +472,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } mPipBoundsState.setBounds(destinationBounds); - mSwipePipToHomeOverlay = overlay; + mPipOverlay = overlay; if (ENABLE_SHELL_TRANSITIONS && overlay != null) { // With Shell transition, the overlay was attached to the remote transition leash, which // will be removed when the current transition is finished, so we need to reparent it @@ -892,7 +884,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final Rect destinationBounds = mPipBoundsState.getBounds(); - final SurfaceControl swipeToHomeOverlay = mSwipePipToHomeOverlay; + final SurfaceControl swipeToHomeOverlay = mPipOverlay; final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper .resetScale(tx, mLeash, destinationBounds) @@ -911,7 +903,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } }, tx); mPipTransitionState.setInSwipePipToHomeTransition(false); - mSwipePipToHomeOverlay = null; + mPipOverlay = null; } private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable, @@ -1126,9 +1118,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } clearWaitForFixedRotation(); - if (mSwipePipToHomeOverlay != null) { - removeContentOverlay(mSwipePipToHomeOverlay, null /* callback */); - mSwipePipToHomeOverlay = null; + if (mPipOverlay != null) { + removeContentOverlay(mPipOverlay, null /* callback */); + mPipOverlay = null; } resetShadowRadius(); mPipTransitionState.setInSwipePipToHomeTransition(false); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 0f3c16220dee..f5f15d81ea44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -465,7 +465,7 @@ public class PipTransition extends PipTransitionController { mSurfaceTransactionHelper.crop(tx, leash, destinationBounds) .resetScale(tx, leash, destinationBounds) .round(tx, leash, true /* applyCornerRadius */); - if (mPipOrganizer.mSwipePipToHomeOverlay != null && !mInitBounds.isEmpty()) { + if (mPipOrganizer.mPipOverlay != null && !mInitBounds.isEmpty()) { // Resetting the scale for pinned task while re-adjusting its crop, // also scales the overlay. So we need to update the overlay leash too. Rect overlayBounds = new Rect(destinationBounds); @@ -476,7 +476,7 @@ public class PipTransition extends PipTransitionController { (destinationBounds.width() - overlaySize) / 2, (destinationBounds.height() - overlaySize) / 2); mSurfaceTransactionHelper.resetScale(tx, - mPipOrganizer.mSwipePipToHomeOverlay, overlayBounds); + mPipOrganizer.mPipOverlay, overlayBounds); } } mInitBounds.setEmpty(); @@ -615,9 +615,9 @@ public class PipTransition extends PipTransitionController { } } // if overlay is present remove it immediately, as exit transition came before it faded out - if (mPipOrganizer.mSwipePipToHomeOverlay != null) { - startTransaction.remove(mPipOrganizer.mSwipePipToHomeOverlay); - clearSwipePipToHomeOverlay(); + if (mPipOrganizer.mPipOverlay != null) { + startTransaction.remove(mPipOrganizer.mPipOverlay); + clearPipOverlay(); } if (pipChange == null) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, @@ -1077,7 +1077,7 @@ public class PipTransition extends PipTransitionController { if (mFixedRotationState == FIXED_ROTATION_CALLBACK && appBounds != null) { mInitBounds.set(appBounds); } - final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay; + final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mPipOverlay; if (swipePipToHomeOverlay != null) { // Launcher fade in the overlay on top of the fullscreen Task. It is possible we // reparent the PIP activity to a new PIP task (in case there are other activities @@ -1106,7 +1106,7 @@ public class PipTransition extends PipTransitionController { sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); if (swipePipToHomeOverlay != null) { mPipOrganizer.fadeOutAndRemoveOverlay(swipePipToHomeOverlay, - this::clearSwipePipToHomeOverlay /* callback */, false /* withStartDelay */); + this::clearPipOverlay /* callback */, false /* withStartDelay */); } mPipTransitionState.setInSwipePipToHomeTransition(false); } @@ -1250,8 +1250,8 @@ public class PipTransition extends PipTransitionController { mPipMenuController.updateMenuBounds(destinationBounds); } - private void clearSwipePipToHomeOverlay() { - mPipOrganizer.mSwipePipToHomeOverlay = null; + private void clearPipOverlay() { + mPipOrganizer.mPipOverlay = null; } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 0367ba160605..d023cea6d19d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -995,16 +995,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { t.show(mOpeningTasks.get(i).mTaskSurface); } for (int i = 0; i < mPausingTasks.size(); ++i) { - if (!sendUserLeaveHint && mPausingTasks.get(i).isLeaf()) { - // This means recents is not *actually* finishing, so of course we gotta - // do special stuff in WMCore to accommodate. - wct.setDoNotPip(mPausingTasks.get(i).mToken); - } - // Since we will reparent out of the leashes, pre-emptively hide the child - // surface to match the leash. Otherwise, there will be a flicker before the - // visibility gets committed in Core when using split-screen (in splitscreen, - // the leaf-tasks are not "independent" so aren't hidden by normal setup). - t.hide(mPausingTasks.get(i).mTaskSurface); + cleanUpPausingOrClosingTask(mPausingTasks.get(i), wct, t, sendUserLeaveHint); + } + for (int i = 0; i < mClosingTasks.size(); ++i) { + cleanUpPausingOrClosingTask(mClosingTasks.get(i), wct, t, sendUserLeaveHint); } if (mPipTransaction != null && sendUserLeaveHint) { SurfaceControl pipLeash = null; @@ -1053,6 +1047,20 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } + private void cleanUpPausingOrClosingTask(TaskState task, WindowContainerTransaction wct, + SurfaceControl.Transaction finishTransaction, boolean sendUserLeaveHint) { + if (!sendUserLeaveHint && task.isLeaf()) { + // This means recents is not *actually* finishing, so of course we gotta + // do special stuff in WMCore to accommodate. + wct.setDoNotPip(task.mToken); + } + // Since we will reparent out of the leashes, pre-emptively hide the child + // surface to match the leash. Otherwise, there will be a flicker before the + // visibility gets committed in Core when using split-screen (in splitscreen, + // the leaf-tasks are not "independent" so aren't hidden by normal setup). + finishTransaction.hide(task.mTaskSurface); + } + @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { } 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 77427d999aaf..96e57e71f05c 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 @@ -223,6 +223,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private boolean mExitSplitScreenOnHide; private boolean mIsDividerRemoteAnimating; private boolean mIsDropEntering; + private boolean mSkipEvictingMainStageChildren; private boolean mIsExiting; private boolean mIsRootTranslucent; @VisibleForTesting @@ -468,6 +469,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // Due to drag already pip task entering split by this method so need to reset flag here. mIsDropEntering = false; + mSkipEvictingMainStageChildren = false; return true; } @@ -572,6 +574,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } + // Don't evict the main stage children as this can race and happen after the activity is + // started into that stage + if (!isSplitScreenVisible()) { + mSkipEvictingMainStageChildren = true; + // Starting the split task without evicting children will bring the single root task + // container forward, so ensure that we hide the divider before we start animate it + setDividerVisibility(false, null); + } + // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = mMainStage.isActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; @@ -600,6 +611,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } + // Don't evict the main stage children as this can race and happen after the activity is + // started into that stage + if (!isSplitScreenVisible()) { + mSkipEvictingMainStageChildren = true; + // Starting the split task without evicting children will bring the single root task + // container forward, so ensure that we hide the divider before we start animate it + setDividerVisibility(false, null); + } + // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = mMainStage.isActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; @@ -1618,7 +1638,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Ensure to evict old splitting tasks because the new split pair might be composed by // one of the splitting tasks, evicting the task when finishing entering transition // won't guarantee to put the task to the indicated new position. - if (!mIsDropEntering) { + if (!mSkipEvictingMainStageChildren) { mMainStage.evictAllChildren(wct); } mMainStage.reparentTopTask(wct); @@ -1680,6 +1700,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, finishT.show(mRootTaskLeash); setSplitsVisible(true); mIsDropEntering = false; + mSkipEvictingMainStageChildren = false; mSplitRequest = null; updateRecentTasksSplitPair(); if (!mLogger.hasStartedSession()) { @@ -1929,6 +1950,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mIsDropEntering) { updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); mIsDropEntering = false; + mSkipEvictingMainStageChildren = false; } else { mShowDecorImmediately = true; mSplitLayout.flingDividerToCenter(); @@ -2123,6 +2145,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mIsDropEntering) { updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); mIsDropEntering = false; + mSkipEvictingMainStageChildren = false; } else { mShowDecorImmediately = true; mSplitLayout.flingDividerToCenter(); @@ -3245,6 +3268,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) { if (!isSplitScreenVisible()) { mIsDropEntering = true; + mSkipEvictingMainStageChildren = true; } if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) { // If split running background, exit split first. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index e03f82526bdb..34c015f05c68 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -330,6 +330,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { continue; } if (isHide) { + if (pending.mType == TRANSIT_TO_BACK) { + // TO_BACK is only used when setting the task view visibility immediately, + // so in that case we can also hide the surface immediately + startTransaction.hide(chg.getLeash()); + } tv.prepareHideAnimation(finishTransaction); } else { tv.prepareCloseAnimation(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index 20ff79f7318e..98d343b66760 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -18,6 +18,7 @@ package com.android.wm.shell.unfold; import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS; @@ -245,23 +246,29 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene public boolean shouldPlayUnfoldAnimation(@NonNull TransitionInfo transitionInfo) { // Unfold animation won't play when animations are disabled if (!ValueAnimator.areAnimatorsEnabled()) return false; + // Only handle transitions that are marked as physical display switch + // See PhysicalDisplaySwitchTransitionLauncher for the conditions + if ((transitionInfo.getFlags() & TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH) == 0) return false; for (int i = 0; i < transitionInfo.getChanges().size(); i++) { final TransitionInfo.Change change = transitionInfo.getChanges().get(i); - if ((change.getFlags() & TransitionInfo.FLAG_IS_DISPLAY) != 0) { - if (change.getEndAbsBounds() == null || change.getStartAbsBounds() == null) { - continue; - } + // We are interested only in display container changes + if ((change.getFlags() & TransitionInfo.FLAG_IS_DISPLAY) == 0) { + continue; + } - // Handle only unfolding, currently we don't have an animation when folding - final int afterArea = - change.getEndAbsBounds().width() * change.getEndAbsBounds().height(); - final int beforeArea = change.getStartAbsBounds().width() - * change.getStartAbsBounds().height(); + // Handle only unfolding, currently we don't have an animation when folding + if (change.getEndAbsBounds() == null || change.getStartAbsBounds() == null) { + continue; + } - if (afterArea > beforeArea) { - return true; - } + final int afterArea = + change.getEndAbsBounds().width() * change.getEndAbsBounds().height(); + final int beforeArea = change.getStartAbsBounds().width() + * change.getStartAbsBounds().height(); + + if (afterArea > beforeArea) { + return true; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 0395a9b78b2b..771876f7ce5d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -182,8 +182,7 @@ public class BackAnimationControllerTest extends ShellTestCase { } private void triggerBackGesture() { - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - doMotionEvent(MotionEvent.ACTION_MOVE, 0); + doStartEvents(0, 0); mController.setTriggerBack(true); } @@ -244,10 +243,7 @@ public class BackAnimationControllerTest extends ShellTestCase { /* enableAnimation = */ true, /* isAnimationCallback = */ false); - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - - // Check that back start and progress is dispatched when first move. - doMotionEvent(MotionEvent.ACTION_MOVE, 100); + doStartEvents(0, 100); simulateRemoteAnimationStart(); @@ -270,10 +266,8 @@ public class BackAnimationControllerTest extends ShellTestCase { /* enableAnimation = */ true, /* isAnimationCallback = */ true); - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - // Check that back start and progress is dispatched when first move. - doMotionEvent(MotionEvent.ACTION_MOVE, 100, 3000); + doStartEvents(0, 100); simulateRemoteAnimationStart(); @@ -359,8 +353,7 @@ public class BackAnimationControllerTest extends ShellTestCase { .injectInputEvent(any(KeyEvent.class), any(Integer.class)); // Verify that we start accepting gestures again once transition finishes. - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - doMotionEvent(MotionEvent.ACTION_MOVE, 100); + doStartEvents(0, 100); simulateRemoteAnimationStart(); verify(mAnimatorCallback).onBackStarted(any()); @@ -399,8 +392,7 @@ public class BackAnimationControllerTest extends ShellTestCase { .injectInputEvent(any(KeyEvent.class), any(Integer.class)); // Verify that we start accepting gestures again once transition finishes. - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - doMotionEvent(MotionEvent.ACTION_MOVE, 100); + doStartEvents(0, 100); simulateRemoteAnimationStart(); verify(mAnimatorCallback).onBackStarted(any()); @@ -427,8 +419,7 @@ public class BackAnimationControllerTest extends ShellTestCase { mShellExecutor.flushAll(); reset(mAnimatorCallback); - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - doMotionEvent(MotionEvent.ACTION_MOVE, 100); + doStartEvents(0, 100); simulateRemoteAnimationStart(); verify(mAnimatorCallback).onBackStarted(any()); } @@ -441,9 +432,7 @@ public class BackAnimationControllerTest extends ShellTestCase { /* enableAnimation = */ true, /* isAnimationCallback = */ false); - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - // Check that back start and progress is dispatched when first move. - doMotionEvent(MotionEvent.ACTION_MOVE, 100); + doStartEvents(0, 100); simulateRemoteAnimationStart(); verify(mAnimatorCallback).onBackStarted(any()); @@ -563,10 +552,8 @@ public class BackAnimationControllerTest extends ShellTestCase { /* enableAnimation = */ true, /* isAnimationCallback = */ false); - doMotionEvent(MotionEvent.ACTION_DOWN, 0); - // Check that back start and progress is dispatched when first move. - doMotionEvent(MotionEvent.ACTION_MOVE, 100); + doStartEvents(0, 100); simulateRemoteAnimationStart(); @@ -593,6 +580,15 @@ public class BackAnimationControllerTest extends ShellTestCase { /* swipeEdge */ BackEvent.EDGE_LEFT); } + /** + * Simulate event sequence that starts a back navigation. + */ + private void doStartEvents(int startX, int moveX) { + doMotionEvent(MotionEvent.ACTION_DOWN, startX); + mController.onPilferPointers(); + doMotionEvent(MotionEvent.ACTION_MOVE, moveX); + } + private void simulateRemoteAnimationStart() throws RemoteException { RemoteAnimationTarget animationTarget = createAnimationTarget(); RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget}; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java index fc1fe1cd0acc..c5e229feaba7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java @@ -18,12 +18,11 @@ package com.android.wm.shell.unfold; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH; import static android.view.WindowManager.TRANSIT_NONE; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -40,22 +39,17 @@ import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; -import com.android.window.flags.FakeFeatureFlagsImpl; -import com.android.window.flags.FeatureFlags; -import com.android.window.flags.Flags; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.sysui.ShellInit; -import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.TransitionInfoBuilder; +import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; import java.util.ArrayList; import java.util.List; @@ -196,6 +190,22 @@ public class UnfoldTransitionHandlerTest { } @Test + public void startAnimation_differentTransitionFromRequestWithResize_doesNotStartAnimation() { + mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo()); + TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); + + boolean animationStarted = mUnfoldTransitionHandler.startAnimation( + mTransition, + createDisplayResizeTransitionInfo(), + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), + finishCallback + ); + + assertThat(animationStarted).isFalse(); + } + + @Test public void startAnimation_differentTransitionFromRequestWithoutUnfold_doesNotStart() { mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo()); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); @@ -403,24 +413,18 @@ public class UnfoldTransitionHandlerTest { } } - static class TestCase { - private final boolean mShouldHandleMixedUnfold; - - public TestCase(boolean shouldHandleMixedUnfold) { - mShouldHandleMixedUnfold = shouldHandleMixedUnfold; - } - - public boolean mixedUnfoldFlagEnabled() { - return mShouldHandleMixedUnfold; - } - - @Override - public String toString() { - return "shouldHandleMixedUnfold flag = " + mShouldHandleMixedUnfold; - } + private TransitionInfo createUnfoldTransitionInfo() { + TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0); + TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class)); + change.setStartAbsBounds(new Rect(0, 0, 10, 10)); + change.setEndAbsBounds(new Rect(0, 0, 100, 100)); + change.setFlags(TransitionInfo.FLAG_IS_DISPLAY); + transitionInfo.addChange(change); + transitionInfo.setFlags(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH); + return transitionInfo; } - private TransitionInfo createUnfoldTransitionInfo() { + private TransitionInfo createDisplayResizeTransitionInfo() { TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0); TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class)); change.setStartAbsBounds(new Rect(0, 0, 10, 10)); diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index 901ea46ba38b..a8ffd2b4dd8f 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -110,6 +110,10 @@ interface ITvInputManager { void pauseRecording(in IBinder sessionToken, in Bundle params, int userId); void resumeRecording(in IBinder sessionToken, in Bundle params, int userId); + // For playback control + void startPlayback(in IBinder sessionToken, int userId); + void stopPlayback(in IBinder sessionToken, int mode, int userId); + // For broadcast info void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId); void removeBroadcastInfo(in IBinder sessionToken, int id, int userId); diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl index 5246f5c4dc1e..e37ee6e342e5 100644 --- a/media/java/android/media/tv/ITvInputSession.aidl +++ b/media/java/android/media/tv/ITvInputSession.aidl @@ -63,6 +63,9 @@ oneway interface ITvInputSession { void timeShiftSetMode(int mode); void timeShiftEnablePositionTracking(boolean enable); + void startPlayback(); + void stopPlayback(int mode); + // For the recording session void startRecording(in Uri programUri, in Bundle params); void stopRecording(); diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index d749b91e3889..ae3ee6535c71 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -79,6 +79,8 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand private static final int DO_TIME_SHIFT_SET_MODE = 30; private static final int DO_SET_TV_MESSAGE_ENABLED = 31; private static final int DO_NOTIFY_TV_MESSAGE = 32; + private static final int DO_STOP_PLAYBACK = 33; + private static final int DO_START_PLAYBACK = 34; private final boolean mIsRecordingSession; private final HandlerCaller mCaller; @@ -286,6 +288,14 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mTvInputSessionImpl.onTvMessageReceived((Integer) args.arg1, (Bundle) args.arg2); break; } + case DO_STOP_PLAYBACK: { + mTvInputSessionImpl.stopPlayback(msg.arg1); + break; + } + case DO_START_PLAYBACK: { + mTvInputSessionImpl.startPlayback(); + break; + } default: { Log.w(TAG, "Unhandled message code: " + msg.what); break; @@ -483,6 +493,17 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand enabled)); } + @Override + public void stopPlayback(int mode) { + mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_STOP_PLAYBACK, mode)); + } + + @Override + public void startPlayback() { + mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_PLAYBACK)); + } + + private final class TvInputEventReceiver extends InputEventReceiver { TvInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 631ab9a2693d..c685a5adb08b 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -339,6 +339,14 @@ public final class TvInputManager { */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = VIDEO_UNAVAILABLE_REASON_END; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * it has been stopped by stopPlayback. + * @hide + */ + public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED, @@ -3302,6 +3310,30 @@ public final class TvInputManager { } } + void stopPlayback(int mode) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.stopPlayback(mToken, mode, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void startPlayback() { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.startPlayback(mToken, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Sends TV messages to the service for testing purposes */ diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 720d9a6291de..55fa51755177 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -34,6 +34,7 @@ import android.graphics.Rect; import android.hardware.hdmi.HdmiDeviceInfo; import android.media.AudioPresentation; import android.media.PlaybackParams; +import android.media.tv.interactive.TvInteractiveAppService; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -1531,6 +1532,32 @@ public abstract class TvInputService extends Service { } /** + * Called when the application requests playback of the Audio, Video, and CC streams to be + * stopped, but the metadata should continue to be filtered. + * + * <p>The metadata that will continue to be filtered includes the PSI + * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1. + * + * <p> Note that this is different form {@link #timeShiftPause()} as should release the + * stream, making it impossible to resume from this position again. + * @param mode + * @hide + */ + public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) { + } + + /** + * Starts playback of the Audio, Video, and CC streams. + * + * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be + * used after stopping playback. This is used to restart playback from the current position + * in the live broadcast. + * @hide + */ + public void onStartPlayback() { + } + + /** * Called when the application requests to play a given recorded TV program. * * @param recordedProgramUri The URI of a recorded TV program. @@ -1993,6 +2020,20 @@ public abstract class TvInputService extends Service { } /** + * Calls {@link #onStopPlayback(int)}. + */ + void stopPlayback(int mode) { + onStopPlayback(mode); + } + + /** + * Calls {@link #onStartPlayback()}. + */ + void startPlayback() { + onStartPlayback(); + } + + /** * Calls {@link #onTimeShiftPlay(Uri)}. */ void timeShiftPlay(Uri recordedProgramUri) { diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 196b5c3112c0..233f96675543 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -37,6 +37,7 @@ import android.media.PlaybackParams; import android.media.tv.TvInputManager.Session; import android.media.tv.TvInputManager.Session.FinishedInputEventCallback; import android.media.tv.TvInputManager.SessionCallback; +import android.media.tv.interactive.TvInteractiveAppService; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -643,6 +644,35 @@ public class TvView extends ViewGroup { } } + /** + * Stops playback of the Audio, Video, and CC streams, but continue filtering the metadata. + * + * <p>The metadata that will continue to be filtered includes the PSI + * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1. + * + * <p> Note that this is different form {@link #timeShiftPause()} as this completely drops + * the stream, making it impossible to resume from this position again. + * @hide + */ + public void stopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) { + if (mSession != null) { + mSession.stopPlayback(mode); + } + } + + /** + * Starts playback of the Audio, Video, and CC streams. + * + * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be + * used after stopping playback. This is used to restart playback from the current position + * in the live broadcast. + * @hide + */ + public void startPlayback() { + if (mSession != null) { + mSession.startPlayback(); + } + } /** * Sends TV messages to the session for testing purposes diff --git a/media/java/android/media/tv/ad/TvAdService.java b/media/java/android/media/tv/ad/TvAdService.java index 61101f07923c..6897a78647c2 100644 --- a/media/java/android/media/tv/ad/TvAdService.java +++ b/media/java/android/media/tv/ad/TvAdService.java @@ -28,6 +28,14 @@ public abstract class TvAdService extends Service { private static final String TAG = "TvAdService"; /** + * Name under which a TvAdService component publishes information about itself. This meta-data + * must reference an XML resource containing an + * <code><{@link android.R.styleable#TvAdService tv-ad-service}></code> tag. + * @hide + */ + public static final String SERVICE_META_DATA = "android.media.tv.ad.service"; + + /** * Base class for derived classes to implement to provide a TV AD session. */ public abstract static class Session implements KeyEvent.Callback { diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 0abf285bd19c..66282dc209ed 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -527,6 +527,15 @@ public class CompanionDeviceActivity extends FragmentActivity implements final Drawable profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile)); + // No need to show permission consent dialog if it is a isSkipPrompt(true) + // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt. + if (mRequest.isSkipPrompt()) { + Log.d(TAG, "Skipping the permission consent dialog."); + mSingleDeviceSpinner.setVisibility(View.GONE); + onUserSelectedDevice(mSelectedDevice); + return; + } + updatePermissionUi(); mProfileIcon.setImageDrawable(profileIcon); @@ -598,6 +607,14 @@ public class CompanionDeviceActivity extends FragmentActivity implements Log.d(TAG, "onDeviceClicked(): " + mSelectedDevice.toShortString()); + // No need to show permission consent dialog if it is a isSkipPrompt(true) + // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt. + if (mRequest.isSkipPrompt()) { + Log.d(TAG, "Skipping the permission consent dialog."); + onUserSelectedDevice(mSelectedDevice); + return; + } + updatePermissionUi(); mSummary.setVisibility(View.VISIBLE); @@ -615,14 +632,6 @@ public class CompanionDeviceActivity extends FragmentActivity implements this, PROFILE_TITLES.get(deviceProfile), mAppLabel, remoteDeviceName); final Spanned summary; - // No need to show permission consent dialog if it is a isSkipPrompt(true) - // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt. - if (mRequest.isSkipPrompt()) { - mSingleDeviceSpinner.setVisibility(View.GONE); - onUserSelectedDevice(mSelectedDevice); - return; - } - if (deviceProfile == null && mRequest.isSingleDevice()) { summary = getHtmlFromResources(this, summaryResourceId, remoteDeviceName); mConstraintList.setVisibility(View.GONE); diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt index f216abb66a64..66bd6f502274 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt @@ -28,7 +28,14 @@ import androidx.compose.material.icons.outlined.Shield import androidx.compose.material.icons.outlined.WarningAmber import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.android.settingslib.spa.framework.common.SettingsEntryBuilder @@ -41,14 +48,14 @@ import com.android.settingslib.spa.gallery.R import com.android.settingslib.spa.widget.card.CardButton import com.android.settingslib.spa.widget.card.CardModel import com.android.settingslib.spa.widget.card.SettingsCard -import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard import com.android.settingslib.spa.widget.card.SettingsCardContent +import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spa.widget.scaffold.RegularScaffold object CardPageProvider : SettingsPageProvider { - override val name = "CardPage" + override val name = "Card" override fun getTitle(arguments: Bundle?) = TITLE @@ -79,10 +86,13 @@ object CardPageProvider : SettingsPageProvider { @Composable private fun SettingsCardWithoutIcon() { + var isVisible by rememberSaveable { mutableStateOf(true) } SettingsCard( CardModel( title = stringResource(R.string.sample_title), text = stringResource(R.string.sample_text), + isVisible = { isVisible }, + onDismiss = { isVisible = false }, buttons = listOf( CardButton(text = "Action") {}, ), @@ -92,21 +102,23 @@ object CardPageProvider : SettingsPageProvider { @Composable fun SampleSettingsCollapsibleCard() { - SettingsCollapsibleCard( - title = "More alerts", - imageVector = Icons.Outlined.Error, - models = listOf( + val context = LocalContext.current + var isVisible0 by rememberSaveable { mutableStateOf(true) } + val cards = remember { + mutableStateListOf( CardModel( - title = stringResource(R.string.sample_title), - text = stringResource(R.string.sample_text), + title = context.getString(R.string.sample_title), + text = context.getString(R.string.sample_text), imageVector = Icons.Outlined.PowerOff, + isVisible = { isVisible0 }, + onDismiss = { isVisible0 = false }, buttons = listOf( CardButton(text = "Action") {}, ) ), CardModel( - title = stringResource(R.string.sample_title), - text = stringResource(R.string.sample_text), + title = context.getString(R.string.sample_title), + text = context.getString(R.string.sample_text), imageVector = Icons.Outlined.Shield, buttons = listOf( CardButton(text = "Action") {}, @@ -114,6 +126,11 @@ object CardPageProvider : SettingsPageProvider { ) ) ) + } + SettingsCollapsibleCard( + title = "More alerts", + imageVector = Icons.Outlined.Error, + models = cards.toList() ) } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt index c143390f269c..993cb4ac85e5 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt @@ -37,6 +37,7 @@ object SettingsDimension { val itemPaddingAround = 8.dp val itemDividerHeight = 32.dp + val iconSmall = 16.dp val iconLarge = 48.dp /** The size when app icon is displayed in list. */ diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt index c113f4332fc3..b18a1bc01388 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt @@ -28,5 +28,14 @@ data class CardModel( val title: String, val text: String, val imageVector: ImageVector? = null, + val isVisible: () -> Boolean = { true }, + + /** + * A dismiss button will be displayed if this is not null. + * + * And this callback will be called when user clicks the button. + */ + val onDismiss: (() -> Unit)? = null, + val buttons: List<CardButton> = emptyList(), ) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt index 43792787c5aa..7eec8888f025 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt @@ -16,28 +16,35 @@ package com.android.settingslib.spa.widget.card +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.WarningAmber import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.android.settingslib.spa.debug.UiModePreviews import com.android.settingslib.spa.framework.theme.SettingsDimension @@ -87,20 +94,31 @@ fun SettingsCard(model: CardModel) { @Composable internal fun SettingsCardImpl(model: CardModel) { - SettingsCardContent { - Column( - modifier = Modifier.padding(SettingsDimension.itemPaddingStart), - verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround) - ) { - CardIcon(model.imageVector) - SettingsTitle(model.title) - SettingsBody(model.text) - Buttons(model.buttons) + AnimatedVisibility(visible = model.isVisible()) { + SettingsCardContent { + Column( + modifier = Modifier.padding(SettingsDimension.itemPaddingStart), + verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround) + ) { + CardHeader(model.imageVector, model.onDismiss) + SettingsTitle(model.title) + SettingsBody(model.text) + Buttons(model.buttons) + } } } } @Composable +fun CardHeader(imageVector: ImageVector?, onDismiss: (() -> Unit)? = null) { + Row(Modifier.fillMaxWidth()) { + CardIcon(imageVector) + Spacer(modifier = Modifier.weight(1f)) + DismissButton(onDismiss) + } +} + +@Composable private fun CardIcon(imageVector: ImageVector?) { if (imageVector != null) { Icon( @@ -113,6 +131,28 @@ private fun CardIcon(imageVector: ImageVector?) { } @Composable +private fun DismissButton(onDismiss: (() -> Unit)?) { + if (onDismiss == null) return + Surface( + shape = CircleShape, + color = MaterialTheme.colorScheme.secondaryContainer, + ) { + IconButton( + onClick = onDismiss, + modifier = Modifier.size(SettingsDimension.itemIconSize) + ) { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = stringResource( + androidx.compose.material3.R.string.m3c_snackbar_dismiss + ), + modifier = Modifier.size(SettingsDimension.iconSmall), + ) + } + } +} + +@Composable private fun Buttons(buttons: List<CardButton>) { if (buttons.isNotEmpty()) { Row( diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt index bf192a18679e..6e36490beac7 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt @@ -58,7 +58,7 @@ fun SettingsCollapsibleCard( var expanded by rememberSaveable { mutableStateOf(false) } SettingsCard { SettingsCardContent { - Header(title, imageVector, models.size, expanded) { expanded = it } + Header(title, imageVector, models.count { it.isVisible() }, expanded) { expanded = it } } AnimatedVisibility(expanded) { Column { diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt index fd3ae49d93c8..beb9433cdbf0 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt @@ -16,10 +16,18 @@ package com.android.settingslib.spa.widget.card +import android.content.Context +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.isNotDisplayed import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat import org.junit.Rule @@ -31,6 +39,8 @@ class SettingsCardTest { @get:Rule val composeTestRule = createComposeRule() + private val context: Context = ApplicationProvider.getApplicationContext() + @Test fun settingsCard_titleDisplayed() { composeTestRule.setContent { @@ -96,6 +106,27 @@ class SettingsCardTest { assertThat(buttonClicked).isTrue() } + @Test + fun settingsCard_dismiss() { + composeTestRule.setContent { + var isVisible by remember { mutableStateOf(true) } + SettingsCard( + CardModel( + title = TITLE, + text = "", + isVisible = { isVisible }, + onDismiss = { isVisible = false }, + ) + ) + } + + composeTestRule.onNodeWithContentDescription( + context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss) + ).performClick() + + composeTestRule.onNodeWithText(TEXT).isNotDisplayed() + } + private companion object { const val TITLE = "Title" const val TEXT = "Text" diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt index efe1c701b1f6..aba9d7be1e91 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt @@ -16,12 +16,20 @@ package com.android.settingslib.spa.widget.card +import android.content.Context import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Error +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.isNotDisplayed import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Rule import org.junit.Test @@ -32,6 +40,8 @@ class SettingsCollapsibleCardTest { @get:Rule val composeTestRule = createComposeRule() + private val context: Context = ApplicationProvider.getApplicationContext() + @Test fun settingsCollapsibleCard_titleDisplayed() { setContent() @@ -62,8 +72,22 @@ class SettingsCollapsibleCardTest { composeTestRule.onNodeWithText(CARD_TEXT).assertIsDisplayed() } + @Test + fun settingsCollapsibleCard_dismiss() { + setContent() + composeTestRule.onNodeWithText(TITLE).performClick() + + composeTestRule.onNodeWithContentDescription( + context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss) + ).performClick() + + composeTestRule.onNodeWithText(CARD_TEXT).isNotDisplayed() + composeTestRule.onNodeWithText("0").assertIsDisplayed() + } + private fun setContent() { composeTestRule.setContent { + var isVisible by rememberSaveable { mutableStateOf(true) } SettingsCollapsibleCard( title = TITLE, imageVector = Icons.Outlined.Error, @@ -71,6 +95,8 @@ class SettingsCollapsibleCardTest { CardModel( title = "", text = CARD_TEXT, + isVisible = { isVisible }, + onDismiss = { isVisible = false }, ) ), ) diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index 02374462f093..c51a9a07332f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -294,32 +294,6 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } /** - * Checks whether add user is disabled on the device - * - * @param context {@link Context} for the calling user. - * - * - * @param userId User to check enforced admin status for. - * - * @return EnforcedAdmin Object containing the enforced admin component and admin user details, - * or {@code null} If adding user is not disabled. - */ - public static EnforcedAdmin checkIfAddUserDisallowed(Context context, int userId) { - final UserManager um = UserManager.get(context); - if (!um.hasUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.of(userId))) { - // Restriction is not enforced. - return null; - } - EnforcedAdmin enforcedAdmin = checkIfRestrictionEnforced(context, - UserManager.DISALLOW_ADD_USER, userId); - if (enforcedAdmin != null) { - return enforcedAdmin; - } - return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction( - UserManager.DISALLOW_ADD_USER); - } - - /** * Check if an application is suspended. * * @return EnforcedAdmin Object containing the enforced admin component and admin user details, diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index 2cb44ec39a23..49ac0f864ed7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -28,7 +28,9 @@ import android.bluetooth.BluetoothLeBroadcast; import android.bluetooth.BluetoothLeBroadcastAssistant; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothLeBroadcastSettings; import android.bluetooth.BluetoothLeBroadcastSubgroup; +import android.bluetooth.BluetoothLeBroadcastSubgroupSettings; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.ContentResolver; @@ -42,10 +44,13 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.android.settingslib.R; +import com.google.common.collect.ImmutableList; + import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -376,6 +381,77 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null); } + /** + * Start the private Broadcast for personal audio sharing or qr code sharing. + * + * <p>The broadcast will use random string for both broadcast name and subgroup program info; + * The broadcast will use random string for broadcast code; The broadcast will only have one + * subgroup due to system limitation; The subgroup language will be null. + * + * <p>If the system started the LE Broadcast, then the system calls the corresponding callback + * {@link BluetoothLeBroadcast.Callback}. + */ + public void startPrivateBroadcast(int quality) { + mNewAppSourceName = "Sharing audio"; + if (mServiceBroadcast == null) { + Log.d(TAG, "The BluetoothLeBroadcast is null when starting the private broadcast."); + return; + } + if (mServiceBroadcast.getAllBroadcastMetadata().size() + >= mServiceBroadcast.getMaximumNumberOfBroadcasts()) { + Log.d(TAG, "Skip starting the broadcast due to number limit."); + return; + } + String programInfo = getProgramInfo(); + if (DEBUG) { + Log.d(TAG, "startBroadcast: language = null ,programInfo = " + programInfo); + } + // Current broadcast framework only support one subgroup + BluetoothLeBroadcastSubgroupSettings subgroupSettings = + buildBroadcastSubgroupSettings(/* language= */ null, programInfo, quality); + BluetoothLeBroadcastSettings settings = + buildBroadcastSettings( + true, // TODO: set to false after framework fix + TextUtils.isEmpty(programInfo) ? null : programInfo, + (mBroadcastCode != null && mBroadcastCode.length > 0) + ? mBroadcastCode + : null, + ImmutableList.of(subgroupSettings)); + mServiceBroadcast.startBroadcast(settings); + } + + private BluetoothLeBroadcastSettings buildBroadcastSettings( + boolean isPublic, + @Nullable String broadcastName, + @Nullable byte[] broadcastCode, + List<BluetoothLeBroadcastSubgroupSettings> subgroupSettingsList) { + BluetoothLeBroadcastSettings.Builder builder = + new BluetoothLeBroadcastSettings.Builder() + .setPublicBroadcast(isPublic) + .setBroadcastName(broadcastName) + .setBroadcastCode(broadcastCode); + for (BluetoothLeBroadcastSubgroupSettings subgroupSettings : subgroupSettingsList) { + builder.addSubgroupSettings(subgroupSettings); + } + return builder.build(); + } + + private BluetoothLeBroadcastSubgroupSettings buildBroadcastSubgroupSettings( + @Nullable String language, @Nullable String programInfo, int quality) { + BluetoothLeAudioContentMetadata metadata = + new BluetoothLeAudioContentMetadata.Builder() + .setLanguage(language) + .setProgramInfo(programInfo) + .build(); + // Current broadcast framework only support one subgroup, thus we still maintain the latest + // metadata to keep legacy UI working. + mBluetoothLeAudioContentMetadata = metadata; + return new BluetoothLeBroadcastSubgroupSettings.Builder() + .setPreferredQuality(quality) + .setContentMetadata(mBluetoothLeAudioContentMetadata) + .build(); + } + public String getProgramInfo() { return mProgramInfo; } diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a26b311b1f8f..facb244a5489 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -61,6 +61,13 @@ flag { } flag { + name: "refactor_get_current_user" + namespace: "systemui" + description: "KeyguardUpdateMonitor.getCurrentUser() was providing outdated results." + bug: "305984787" +} + +flag { name: "notification_throttle_hun" namespace: "systemui" description: "During notification avalanche, throttle HUNs showing in fast succession." diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index e538e093e60f..2944bd9f9a8e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -174,8 +174,8 @@ private fun <T> computeValue( lerp: (T, T, Float) -> T, canOverflow: Boolean, ): T { - val state = layoutImpl.state.transitionState - if (state !is TransitionState.Transition || !layoutImpl.isTransitionReady(state)) { + val transition = layoutImpl.state.currentTransition + if (transition == null || !layoutImpl.isTransitionReady(transition)) { return sharedValue.value } @@ -191,10 +191,11 @@ private fun <T> computeValue( return value as Element.SharedValue<T> } - val fromValue = sceneValue(state.fromScene) - val toValue = sceneValue(state.toScene) + val fromValue = sceneValue(transition.fromScene) + val toValue = sceneValue(transition.toScene) return if (fromValue != null && toValue != null) { - val progress = if (canOverflow) state.progress else state.progress.coerceIn(0f, 1f) + val progress = + if (canOverflow) transition.progress else transition.progress.coerceIn(0f, 1f) lerp(fromValue.value, toValue.value, progress) } else if (fromValue != null) { fromValue.value diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt index de69c37d4630..ba6d00e3b7f5 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -28,11 +28,11 @@ import kotlinx.coroutines.launch * the currently running transition, if there is one. */ internal fun CoroutineScope.animateToScene( - layoutImpl: SceneTransitionLayoutImpl, + layoutState: SceneTransitionLayoutStateImpl, target: SceneKey, ) { - val state = layoutImpl.state.transitionState - if (state.currentScene == target) { + val transitionState = layoutState.transitionState + if (transitionState.currentScene == target) { // This can happen in 3 different situations, for which there isn't anything else to do: // 1. There is no ongoing transition and [target] is already the current scene. // 2. The user is swiping to [target] from another scene and released their pointer such @@ -44,44 +44,47 @@ internal fun CoroutineScope.animateToScene( return } - when (state) { - is TransitionState.Idle -> animate(layoutImpl, target) + when (transitionState) { + is TransitionState.Idle -> animate(layoutState, target) is TransitionState.Transition -> { // A transition is currently running: first check whether `transition.toScene` or // `transition.fromScene` is the same as our target scene, in which case the transition // can be accelerated or reversed to end up in the target state. - if (state.toScene == target) { + if (transitionState.toScene == target) { // The user is currently swiping to [target] but didn't release their pointer yet: // animate the progress to `1`. - check(state.fromScene == state.currentScene) - val progress = state.progress + check(transitionState.fromScene == transitionState.currentScene) + val progress = transitionState.progress if ((1f - progress).absoluteValue < ProgressVisibilityThreshold) { - // The transition is already finished (progress ~= 1): no need to animate. - layoutImpl.state.transitionState = TransitionState.Idle(state.currentScene) + // The transition is already finished (progress ~= 1): no need to animate. We + // finish the current transition early to make sure that the current state + // change is committed. + layoutState.finishTransition(transitionState, transitionState.currentScene) } else { // The transition is in progress: start the canned animation at the same // progress as it was in. // TODO(b/290184746): Also take the current velocity into account. - animate(layoutImpl, target, startProgress = progress) + animate(layoutState, target, startProgress = progress) } return } - if (state.fromScene == target) { + if (transitionState.fromScene == target) { // There is a transition from [target] to another scene: simply animate the same // transition progress to `0`. - check(state.toScene == state.currentScene) - val progress = state.progress + check(transitionState.toScene == transitionState.currentScene) + val progress = transitionState.progress if (progress.absoluteValue < ProgressVisibilityThreshold) { - // The transition is at progress ~= 0: no need to animate. - layoutImpl.state.transitionState = TransitionState.Idle(state.currentScene) + // The transition is at progress ~= 0: no need to animate.We finish the current + // transition early to make sure that the current state change is committed. + layoutState.finishTransition(transitionState, transitionState.currentScene) } else { // TODO(b/290184746): Also take the current velocity into account. - animate(layoutImpl, target, startProgress = progress, reversed = true) + animate(layoutState, target, startProgress = progress, reversed = true) } return @@ -89,27 +92,22 @@ internal fun CoroutineScope.animateToScene( // Generic interruption; the current transition is neither from or to [target]. // TODO(b/290930950): Better handle interruptions here. - animate(layoutImpl, target) + animate(layoutState, target) } } } private fun CoroutineScope.animate( - layoutImpl: SceneTransitionLayoutImpl, + layoutState: SceneTransitionLayoutStateImpl, target: SceneKey, startProgress: Float = 0f, reversed: Boolean = false, ) { - val fromScene = layoutImpl.state.transitionState.currentScene + val fromScene = layoutState.transitionState.currentScene val isUserInput = - (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput + (layoutState.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput ?: false - val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec - val visibilityThreshold = - (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold - val animatable = Animatable(startProgress, visibilityThreshold = visibilityThreshold) - val targetProgress = if (reversed) 0f else 1f val transition = if (reversed) { @@ -119,7 +117,6 @@ private fun CoroutineScope.animate( currentScene = target, isInitiatedByUserInput = isUserInput, isUserInputOngoing = false, - animatable = animatable, ) } else { OneOffTransition( @@ -128,21 +125,27 @@ private fun CoroutineScope.animate( currentScene = target, isInitiatedByUserInput = isUserInput, isUserInputOngoing = false, - animatable = animatable, ) } - // Change the current layout state to use this new transition. - layoutImpl.state.transitionState = transition + // Change the current layout state to start this new transition. This will compute the + // TransformationSpec associated to this transition, which we need to initialize the Animatable + // that will actually animate it. + layoutState.startTransition(transition) + + // The transformation now contains the spec that we should use to instantiate the Animatable. + val animationSpec = layoutState.transformationSpec.progressSpec + val visibilityThreshold = + (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold + val animatable = + Animatable(startProgress, visibilityThreshold = visibilityThreshold).also { + transition.animatable = it + } // Animate the progress to its target value. launch { animatable.animateTo(targetProgress, animationSpec) - - // Unless some other external state change happened, the state should now be idle. - if (layoutImpl.state.transitionState == transition) { - layoutImpl.state.transitionState = TransitionState.Idle(target) - } + layoutState.finishTransition(transition, target) } } @@ -152,8 +155,16 @@ private class OneOffTransition( override val currentScene: SceneKey, override val isInitiatedByUserInput: Boolean, override val isUserInputOngoing: Boolean, - private val animatable: Animatable<Float, AnimationVector1D>, ) : TransitionState.Transition(fromScene, toScene) { + /** + * The animatable used to animate this transition. + * + * Note: This is lateinit because we need to first create this Transition object so that + * [SceneTransitionLayoutState] can compute the transformations and animation spec associated to + * it, which is need to initialize this Animatable. + */ + lateinit var animatable: Animatable<Float, AnimationVector1D> + override val progress: Float get() = animatable.value } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index 431a8aef6d3d..5dc1079e8b56 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -181,15 +181,11 @@ private data class ElementModifier( } internal class ElementNode( - layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, - element: Element, - sceneValues: Element.TargetValues, + private var layoutImpl: SceneTransitionLayoutImpl, + private var scene: Scene, + private var element: Element, + private var sceneValues: Element.TargetValues, ) : Modifier.Node(), DrawModifierNode { - private var layoutImpl: SceneTransitionLayoutImpl = layoutImpl - private var scene: Scene = scene - private var element: Element = element - private var sceneValues: Element.TargetValues = sceneValues override fun onAttach() { super.onAttach() @@ -283,26 +279,27 @@ private fun shouldDrawElement( scene: Scene, element: Element, ): Boolean { - val state = layoutImpl.state.transitionState + val transition = layoutImpl.state.currentTransition // Always draw the element if there is no ongoing transition or if the element is not shared. if ( - state !is TransitionState.Transition || - !layoutImpl.isTransitionReady(state) || - state.fromScene !in element.sceneValues || - state.toScene !in element.sceneValues + transition == null || + !layoutImpl.isTransitionReady(transition) || + transition.fromScene !in element.sceneValues || + transition.toScene !in element.sceneValues ) { return true } - val sharedTransformation = sharedElementTransformation(layoutImpl, state, element.key) + val sharedTransformation = + sharedElementTransformation(layoutImpl.state, transition, element.key) if (sharedTransformation?.enabled == false) { return true } return shouldDrawOrComposeSharedElement( layoutImpl, - state, + transition, scene.key, element.key, sharedTransformation, @@ -331,21 +328,21 @@ internal fun shouldDrawOrComposeSharedElement( } private fun isSharedElementEnabled( - layoutImpl: SceneTransitionLayoutImpl, + layoutState: SceneTransitionLayoutStateImpl, transition: TransitionState.Transition, element: ElementKey, ): Boolean { - return sharedElementTransformation(layoutImpl, transition, element)?.enabled ?: true + return sharedElementTransformation(layoutState, transition, element)?.enabled ?: true } internal fun sharedElementTransformation( - layoutImpl: SceneTransitionLayoutImpl, + layoutState: SceneTransitionLayoutStateImpl, transition: TransitionState.Transition, element: ElementKey, ): SharedElementTransformation? { - val spec = layoutImpl.transitions.transitionSpec(transition.fromScene, transition.toScene) - val sharedInFromScene = spec.transformations(element, transition.fromScene).shared - val sharedInToScene = spec.transformations(element, transition.toScene).shared + val transformationSpec = layoutState.transformationSpec + val sharedInFromScene = transformationSpec.transformations(element, transition.fromScene).shared + val sharedInToScene = transformationSpec.transformations(element, transition.toScene).shared // The sharedElement() transformation must either be null or be the same in both scenes. if (sharedInFromScene != sharedInToScene) { @@ -371,13 +368,9 @@ private fun isElementOpaque( scene: Scene, sceneValues: Element.TargetValues, ): Boolean { - val state = layoutImpl.state.transitionState - - if (state !is TransitionState.Transition) { - return true - } + val transition = layoutImpl.state.currentTransition ?: return true - if (!layoutImpl.isTransitionReady(state)) { + if (!layoutImpl.isTransitionReady(transition)) { val lastValue = sceneValues.lastValues.alpha.takeIf { it != Element.AlphaUnspecified } ?: element.lastSharedValues.alpha.takeIf { it != Element.AlphaUnspecified } ?: 1f @@ -385,8 +378,8 @@ private fun isElementOpaque( return lastValue == 1f } - val fromScene = state.fromScene - val toScene = state.toScene + val fromScene = transition.fromScene + val toScene = transition.toScene val fromValues = element.sceneValues[fromScene] val toValues = element.sceneValues[toScene] @@ -395,14 +388,11 @@ private fun isElementOpaque( } val isSharedElement = fromValues != null && toValues != null - if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) { + if (isSharedElement && isSharedElementEnabled(layoutImpl.state, transition, element.key)) { return true } - return layoutImpl.transitions - .transitionSpec(fromScene, toScene) - .transformations(element.key, scene.key) - .alpha == null + return layoutImpl.state.transformationSpec.transformations(element.key, scene.key).alpha == null } /** @@ -607,24 +597,22 @@ private inline fun <T> computeValue( lastValue: () -> T, lerp: (T, T, Float) -> T, ): T { - val state = layoutImpl.state.transitionState - - // There is no ongoing transition. - if (state !is TransitionState.Transition) { - // Even if this element SceneTransitionLayout is not animated, the layout itself might be - // animated (e.g. by another parent SceneTransitionLayout), in which case this element still - // need to participate in the layout phase. - return currentValue() - } + val transition = + layoutImpl.state.currentTransition + // There is no ongoing transition. Even if this element SceneTransitionLayout is not + // animated, the layout itself might be animated (e.g. by another parent + // SceneTransitionLayout), in which case this element still need to participate in the + // layout phase. + ?: return currentValue() // A transition was started but it's not ready yet (not all elements have been composed/laid // out yet). Use the last value that was set, to make sure elements don't unexpectedly jump. - if (!layoutImpl.isTransitionReady(state)) { + if (!layoutImpl.isTransitionReady(transition)) { return lastValue() } - val fromScene = state.fromScene - val toScene = state.toScene + val fromScene = transition.fromScene + val toScene = transition.toScene val fromValues = element.sceneValues[fromScene] val toValues = element.sceneValues[toScene] @@ -638,21 +626,17 @@ private inline fun <T> computeValue( // TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared // elements follow the finger direction. val isSharedElement = fromValues != null && toValues != null - if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) { + if (isSharedElement && isSharedElementEnabled(layoutImpl.state, transition, element.key)) { val start = sceneValue(fromValues!!) val end = sceneValue(toValues!!) // Make sure we don't read progress if values are the same and we don't need to interpolate, // so we don't invalidate the phase where this is read. - return if (start == end) start else lerp(start, end, state.progress) + return if (start == end) start else lerp(start, end, transition.progress) } val transformation = - transformation( - layoutImpl.transitions - .transitionSpec(fromScene, toScene) - .transformations(element.key, scene.key) - ) + transformation(layoutImpl.state.transformationSpec.transformations(element.key, scene.key)) // If there is no transformation explicitly associated to this element value, let's use // the value given by the system (like the current position and size given by the layout // pass). @@ -675,7 +659,7 @@ private inline fun <T> computeValue( scene, element, sceneValues, - state, + transition, idleValue, ) @@ -685,7 +669,7 @@ private inline fun <T> computeValue( return targetValue } - val progress = state.progress + val progress = transition.progress // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range. val rangeProgress = transformation.range?.progress(progress) ?: progress 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 7029da2edb0d..306f27626e19 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 @@ -120,17 +120,13 @@ private fun shouldComposeMovableElement( scene: SceneKey, element: Element, ): Boolean { - val transitionState = layoutImpl.state.transitionState - - // If we are idle, there is only one [scene] that is composed so we can compose our movable - // content here. - if (transitionState is TransitionState.Idle) { - check(transitionState.currentScene == scene) - return true - } - - val fromScene = (transitionState as TransitionState.Transition).fromScene - val toScene = transitionState.toScene + val transition = + layoutImpl.state.currentTransition + // If we are idle, there is only one [scene] that is composed so we can compose our + // movable content here. + ?: return true + val fromScene = transition.fromScene + val toScene = transition.toScene val fromReady = layoutImpl.isSceneReady(fromScene) val toReady = layoutImpl.isSceneReady(toScene) @@ -181,10 +177,10 @@ private fun shouldComposeMovableElement( return shouldDrawOrComposeSharedElement( layoutImpl, - transitionState, + transition, scene, element.key, - sharedElementTransformation(layoutImpl, transitionState, element.key), + sharedElementTransformation(layoutImpl.state, transition, element.key), ) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt index 32025b4f1258..e78f3266d664 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt @@ -179,7 +179,8 @@ private fun scenePriorityNestedScrollConnection( bottomOrRightBehavior: NestedScrollBehavior, ) = SceneNestedScrollHandler( - gestureHandler = layoutImpl.gestureHandler(orientation = orientation), + layoutImpl = layoutImpl, + orientation = orientation, topOrLeftBehavior = topOrLeftBehavior, bottomOrRightBehavior = bottomOrRightBehavior, ) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index 91decf4d8b7e..338557d0942e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -41,14 +41,9 @@ internal class SceneGestureHandler( internal val orientation: Orientation, private val coroutineScope: CoroutineScope, ) { + private val layoutState = layoutImpl.state val draggable: DraggableHandler = SceneDraggableHandler(this) - internal var transitionState - get() = layoutImpl.state.transitionState - set(value) { - layoutImpl.state.transitionState = value - } - private var _swipeTransition: SwipeTransition? = null internal var swipeTransition: SwipeTransition get() = _swipeTransition ?: error("SwipeTransition needs to be initialized") @@ -57,27 +52,26 @@ internal class SceneGestureHandler( } private fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) { - if (isDrivingTransition || force) transitionState = newTransition + if (isDrivingTransition || force) layoutState.startTransition(newTransition) swipeTransition = newTransition } - internal val currentScene: Scene - get() = layoutImpl.scene(transitionState.currentScene) - internal val isDrivingTransition - get() = transitionState == _swipeTransition + get() = layoutState.transitionState == _swipeTransition /** * The velocity threshold at which the intent of the user is to swipe up or down. It is the same * as SwipeableV2Defaults.VelocityThreshold. */ - internal val velocityThreshold = with(layoutImpl.density) { 125.dp.toPx() } + internal val velocityThreshold: Float + get() = with(layoutImpl.density) { 125.dp.toPx() } /** * The positional threshold at which the intent of the user is to swipe to the next scene. It is * the same as SwipeableV2Defaults.PositionalThreshold. */ - private val positionalThreshold = with(layoutImpl.density) { 56.dp.toPx() } + private val positionalThreshold + get() = with(layoutImpl.density) { 56.dp.toPx() } internal var gestureWithPriority: Any? = null @@ -98,18 +92,18 @@ internal class SceneGestureHandler( return } - val transition = transitionState - if (transition is TransitionState.Transition) { + val transitionState = layoutState.transitionState + if (transitionState is TransitionState.Transition) { // TODO(b/290184746): Better handle interruptions here if state != idle. Log.w( TAG, "start from TransitionState.Transition is not fully supported: from" + - " ${transition.fromScene} to ${transition.toScene} " + - "(progress ${transition.progress})" + " ${transitionState.fromScene} to ${transitionState.toScene} " + + "(progress ${transitionState.progress})" ) } - val fromScene = currentScene + val fromScene = layoutImpl.scene(transitionState.currentScene) setCurrentActions(fromScene, startedPosition, pointersDown) val (targetScene, distance) = @@ -364,7 +358,7 @@ internal class SceneGestureHandler( findTargetSceneAndDistanceStrict(fromScene, velocity) ?: run { // We will not animate - transitionState = TransitionState.Idle(fromScene.key) + layoutState.finishTransition(swipeTransition, idleScene = fromScene.key) return } @@ -439,14 +433,7 @@ internal class SceneGestureHandler( ) swipeTransition.finishOffsetAnimation() - - // Now that the animation is done, the state should be idle. Note that if the state - // was changed since this animation started, some external code changed it and we - // shouldn't do anything here. Note also that this job will be cancelled in the case - // where the user intercepts this swipe. - if (isDrivingTransition) { - transitionState = TransitionState.Idle(targetScene) - } + layoutState.finishTransition(swipeTransition, targetScene) } } } @@ -539,10 +526,14 @@ private class SceneDraggableHandler( } internal class SceneNestedScrollHandler( - private val gestureHandler: SceneGestureHandler, + private val layoutImpl: SceneTransitionLayoutImpl, + private val orientation: Orientation, private val topOrLeftBehavior: NestedScrollBehavior, private val bottomOrRightBehavior: NestedScrollBehavior, ) : NestedScrollHandler { + private val layoutState = layoutImpl.state + private val gestureHandler = layoutImpl.gestureHandler(orientation) + override val connection: PriorityNestedScrollConnection = nestedScrollConnection() private fun nestedScrollConnection(): PriorityNestedScrollConnection { @@ -553,7 +544,7 @@ internal class SceneNestedScrollHandler( val actionUpOrLeft = Swipe( direction = - when (gestureHandler.orientation) { + when (orientation) { Orientation.Horizontal -> SwipeDirection.Left Orientation.Vertical -> SwipeDirection.Up }, @@ -563,7 +554,7 @@ internal class SceneNestedScrollHandler( val actionDownOrRight = Swipe( direction = - when (gestureHandler.orientation) { + when (orientation) { Orientation.Horizontal -> SwipeDirection.Right Orientation.Vertical -> SwipeDirection.Down }, @@ -571,7 +562,7 @@ internal class SceneNestedScrollHandler( ) fun hasNextScene(amount: Float): Boolean { - val fromScene = gestureHandler.currentScene + val fromScene = layoutImpl.scene(layoutState.transitionState.currentScene) val nextScene = when { amount < 0f -> fromScene.userActions[actionUpOrLeft] @@ -582,7 +573,7 @@ internal class SceneNestedScrollHandler( } return PriorityNestedScrollConnection( - orientation = gestureHandler.orientation, + orientation = orientation, canStartPreScroll = { offsetAvailable, offsetBeforeStart -> canChangeScene = offsetBeforeStart == 0f @@ -590,8 +581,9 @@ internal class SceneNestedScrollHandler( canChangeScene && gestureHandler.isDrivingTransition && offsetAvailable != 0f if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false - val progress = gestureHandler.swipeTransition.progress - val threshold = gestureHandler.layoutImpl.transitionInterceptionThreshold + val swipeTransition = gestureHandler.swipeTransition + val progress = swipeTransition.progress + val threshold = layoutImpl.transitionInterceptionThreshold fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold // The transition is always between 0 and 1. If it is close to either of these @@ -599,9 +591,8 @@ internal class SceneNestedScrollHandler( // The progress value can go beyond this range in the case of overscroll. val shouldSnapToIdle = isProgressCloseTo(0f) || isProgressCloseTo(1f) if (shouldSnapToIdle) { - gestureHandler.swipeTransition.cancelOffsetAnimation() - gestureHandler.transitionState = - TransitionState.Idle(gestureHandler.swipeTransition.currentScene) + swipeTransition.cancelOffsetAnimation() + layoutState.finishTransition(swipeTransition, swipeTransition.currentScene) } // Start only if we cannot consume this event diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 239971ff6be8..3608e374fdbc 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -19,6 +19,8 @@ package com.android.compose.animation.scene import androidx.annotation.FloatRange import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.State import androidx.compose.runtime.remember @@ -27,6 +29,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.platform.LocalDensity +import kotlinx.coroutines.channels.Channel /** * [SceneTransitionLayout] is a container that automatically animates its content whenever @@ -266,24 +269,45 @@ internal fun SceneTransitionLayoutForTesting( val coroutineScope = rememberCoroutineScope() val layoutImpl = remember { SceneTransitionLayoutImpl( + state = state as SceneTransitionLayoutStateImpl, onChangeScene = onChangeScene, - builder = scenes, - transitions = transitions, - state = state, density = density, edgeDetector = edgeDetector, transitionInterceptionThreshold = transitionInterceptionThreshold, + builder = scenes, coroutineScope = coroutineScope, ) .also { onLayoutImpl?.invoke(it) } } - layoutImpl.onChangeScene = onChangeScene - layoutImpl.transitions = transitions - layoutImpl.density = density - layoutImpl.edgeDetector = edgeDetector + val targetSceneChannel = remember { Channel<SceneKey>(Channel.CONFLATED) } + SideEffect { + if (state != layoutImpl.state) { + error( + "This SceneTransitionLayout was bound to a different SceneTransitionLayoutState" + + " that was used when creating it, which is not supported" + ) + } + + layoutImpl.onChangeScene = onChangeScene + (state as SceneTransitionLayoutStateImpl).transitions = transitions + layoutImpl.density = density + layoutImpl.edgeDetector = edgeDetector + layoutImpl.updateScenes(scenes) + + state.transitions = transitions + + targetSceneChannel.trySend(currentScene) + } + + LaunchedEffect(targetSceneChannel) { + for (newKey in targetSceneChannel) { + // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame + // late. + val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey + animateToScene(layoutImpl.state, newKey) + } + } - layoutImpl.setScenes(scenes) - layoutImpl.setCurrentScene(currentScene) layoutImpl.Content(modifier) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 00e33e24c41e..c99c3250bbb1 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -22,13 +22,8 @@ import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.SideEffect import androidx.compose.runtime.Stable -import androidx.compose.runtime.getValue import androidx.compose.runtime.key -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -40,36 +35,40 @@ import androidx.compose.ui.unit.IntSize import androidx.compose.ui.util.fastForEach import com.android.compose.ui.util.lerp import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.channels.Channel @Stable internal class SceneTransitionLayoutImpl( - onChangeScene: (SceneKey) -> Unit, + internal val state: SceneTransitionLayoutStateImpl, + internal var onChangeScene: (SceneKey) -> Unit, + internal var density: Density, + internal var edgeDetector: EdgeDetector, + internal var transitionInterceptionThreshold: Float, builder: SceneTransitionLayoutScope.() -> Unit, - transitions: SceneTransitions, - internal val state: SceneTransitionLayoutState, - density: Density, - edgeDetector: EdgeDetector, - transitionInterceptionThreshold: Float, coroutineScope: CoroutineScope, ) { - internal val scenes = SnapshotStateMap<SceneKey, Scene>() + internal val scenes = mutableMapOf<SceneKey, Scene>() + + /** + * The map of [Element]s. + * + * Note that this map is *mutated* directly during composition, so it is a [SnapshotStateMap] to + * make sure that mutations are reverted if composition is cancelled. + */ internal val elements = SnapshotStateMap<ElementKey, Element>() - /** The scenes that are "ready", i.e. they were composed and fully laid-out at least once. */ + /** + * The scenes that are "ready", i.e. they were composed and fully laid-out at least once. + * + * Note that this map is *read* during composition, so it is a [SnapshotStateMap] to make sure + * that we recompose when modifications are made to this map. + */ private val readyScenes = SnapshotStateMap<SceneKey, Boolean>() - internal var onChangeScene by mutableStateOf(onChangeScene) - internal var transitions by mutableStateOf(transitions) - internal var density: Density by mutableStateOf(density) - internal var edgeDetector by mutableStateOf(edgeDetector) - internal var transitionInterceptionThreshold by mutableStateOf(transitionInterceptionThreshold) - private val horizontalGestureHandler: SceneGestureHandler private val verticalGestureHandler: SceneGestureHandler init { - setScenes(builder) + updateScenes(builder) // SceneGestureHandler must wait for the scenes to be initialized, in order to access the // current scene (required for SwipeTransition). @@ -98,7 +97,7 @@ internal class SceneTransitionLayoutImpl( return scenes[key] ?: error("Scene $key is not configured") } - internal fun setScenes(builder: SceneTransitionLayoutScope.() -> Unit) { + internal fun updateScenes(builder: SceneTransitionLayoutScope.() -> Unit) { // Keep a reference of the current scenes. After processing [builder], the scenes that were // not configured will be removed. val scenesToRemove = scenes.keys.toMutableSet() @@ -141,20 +140,6 @@ internal class SceneTransitionLayoutImpl( } @Composable - internal fun setCurrentScene(key: SceneKey) { - val channel = remember { Channel<SceneKey>(Channel.CONFLATED) } - SideEffect { channel.trySend(key) } - LaunchedEffect(channel) { - for (newKey in channel) { - // Inspired by AnimateAsState.kt: let's poll the last value to avoid being one frame - // late. - val newKey = channel.tryReceive().getOrNull() ?: newKey - animateToScene(this@SceneTransitionLayoutImpl, newKey) - } - } - } - - @Composable @OptIn(ExperimentalComposeUiApi::class) internal fun Content(modifier: Modifier) { Box( @@ -171,14 +156,14 @@ internal class SceneTransitionLayoutImpl( val width: Int val height: Int - val state = state.transitionState - if (state !is TransitionState.Transition) { + val transition = state.currentTransition + if (transition == null) { width = placeable.width height = placeable.height } else { // Interpolate the size. - val fromSize = scene(state.fromScene).targetSize - val toSize = scene(state.toScene).targetSize + val fromSize = scene(transition.fromScene).targetSize + val toSize = scene(transition.toScene).targetSize // Optimization: make sure we don't read state.progress if fromSize == // toSize to avoid running this code every frame when the layout size does @@ -187,7 +172,7 @@ internal class SceneTransitionLayoutImpl( width = fromSize.width height = fromSize.height } else { - val size = lerp(fromSize, toSize, state.progress) + val size = lerp(fromSize, toSize, transition.progress) width = size.width.coerceAtLeast(0) height = size.height.coerceAtLeast(0) } @@ -228,13 +213,12 @@ internal class SceneTransitionLayoutImpl( scene.Content( Modifier.drawWithContent { - when (val state = state.transitionState) { - is TransitionState.Idle -> drawContent() - is TransitionState.Transition -> { - // Don't draw scenes that are not ready yet. - if (readyScenes.containsKey(key)) { - drawContent() - } + if (state.currentTransition == null) { + drawContent() + } else { + // Don't draw scenes that are not ready yet. + if (readyScenes.containsKey(key)) { + drawContent() } } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 623725582a9d..d1ba582d6c23 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -23,31 +23,32 @@ import androidx.compose.runtime.setValue /** The state of a [SceneTransitionLayout]. */ @Stable -class SceneTransitionLayoutState(initialScene: SceneKey) { +sealed interface SceneTransitionLayoutState { /** * The current [TransitionState]. All values read here are backed by the Snapshot system. * * To observe those values outside of Compose/the Snapshot system, use * [SceneTransitionLayoutState.observableTransitionState] instead. */ - var transitionState: TransitionState by mutableStateOf(TransitionState.Idle(initialScene)) + val transitionState: TransitionState + + /** The current transition, or `null` if we are idle. */ + val currentTransition: TransitionState.Transition? + get() = transitionState as? TransitionState.Transition /** - * Whether we are transitioning, optionally restricting the check to the transition between - * [from] and [to]. + * Whether we are transitioning. If [from] or [to] is empty, we will also check that they match + * the scenes we are animating from and/or to. */ - fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean { - val transition = transitionState as? TransitionState.Transition ?: return false - - return (from == null || transition.fromScene == from) && - (to == null || transition.toScene == to) - } + fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean /** Whether we are transitioning from [scene] to [other], or from [other] to [scene]. */ - fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean { - return isTransitioning(from = scene, to = other) || - isTransitioning(from = other, to = scene) - } + fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean +} + +/** Create a new [SceneTransitionLayoutState] that is currently idle at scene [currentScene]. */ +fun SceneTransitionLayoutState(currentScene: SceneKey): SceneTransitionLayoutState { + return SceneTransitionLayoutStateImpl(currentScene, SceneTransitions.Empty) } @Stable @@ -93,3 +94,50 @@ sealed interface TransitionState { abstract val isUserInputOngoing: Boolean } } + +internal class SceneTransitionLayoutStateImpl( + initialScene: SceneKey, + internal var transitions: SceneTransitions, +) : SceneTransitionLayoutState { + override var transitionState: TransitionState by + mutableStateOf(TransitionState.Idle(initialScene)) + private set + + /** + * The current [transformationSpec] associated to [transitionState]. Accessing this value makes + * sense only if [transitionState] is a [TransitionState.Transition]. + */ + internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty + + override fun isTransitioning(from: SceneKey?, to: SceneKey?): Boolean { + val transition = currentTransition ?: return false + return (from == null || transition.fromScene == from) && + (to == null || transition.toScene == to) + } + + override fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean { + return isTransitioning(from = scene, to = other) || + isTransitioning(from = other, to = scene) + } + + /** Start a new [transition], instantly interrupting any ongoing transition if there was one. */ + internal fun startTransition(transition: TransitionState.Transition) { + // Compute the [TransformationSpec] when the transition starts. + transformationSpec = + transitions + .transitionSpec(transition.fromScene, transition.toScene) + .transformationSpec() + + transitionState = transition + } + + /** + * Notify that [transition] was finished and that we should settle to [idleScene]. This will do + * nothing if [transition] was interrupted since it was started. + */ + internal fun finishTransition(transition: TransitionState.Transition, idleScene: SceneKey) { + if (transitionState == transition) { + transitionState = TransitionState.Idle(idleScene) + } + } +} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt index f91895bb0e05..3a55567d69bb 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt @@ -18,11 +18,9 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.AnimationSpec import androidx.compose.animation.core.snap -import androidx.compose.runtime.Stable import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.util.fastForEach -import androidx.compose.ui.util.fastMap import com.android.compose.animation.scene.transformation.AnchoredSize import com.android.compose.animation.scene.transformation.AnchoredTranslate import com.android.compose.animation.scene.transformation.DrawScale @@ -36,16 +34,17 @@ import com.android.compose.animation.scene.transformation.Transformation import com.android.compose.animation.scene.transformation.Translate /** The transitions configuration of a [SceneTransitionLayout]. */ -class SceneTransitions( - internal val transitionSpecs: List<TransitionSpec>, +class SceneTransitions +internal constructor( + internal val transitionSpecs: List<TransitionSpecImpl>, ) { - private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpec>>() + private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpecImpl>>() - internal fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpec { + internal fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpecImpl { return cache.getOrPut(from) { mutableMapOf() }.getOrPut(to) { findSpec(from, to) } } - private fun findSpec(from: SceneKey, to: SceneKey): TransitionSpec { + private fun findSpec(from: SceneKey, to: SceneKey): TransitionSpecImpl { val spec = transition(from, to) { it.from == from && it.to == to } if (spec != null) { return spec @@ -53,7 +52,7 @@ class SceneTransitions( val reversed = transition(from, to) { it.from == to && it.to == from } if (reversed != null) { - return reversed.reverse() + return reversed.reversed() } val relaxedSpec = @@ -67,16 +66,16 @@ class SceneTransitions( return transition(from, to) { (it.from == to && it.to == null) || (it.to == from && it.from == null) } - ?.reverse() + ?.reversed() ?: defaultTransition(from, to) } private fun transition( from: SceneKey, to: SceneKey, - filter: (TransitionSpec) -> Boolean, - ): TransitionSpec? { - var match: TransitionSpec? = null + filter: (TransitionSpecImpl) -> Boolean, + ): TransitionSpecImpl? { + var match: TransitionSpecImpl? = null transitionSpecs.fastForEach { spec -> if (filter(spec)) { if (match != null) { @@ -89,28 +88,88 @@ class SceneTransitions( } private fun defaultTransition(from: SceneKey, to: SceneKey) = - TransitionSpec(from, to, emptyList(), snap()) + TransitionSpecImpl(from, to, TransformationSpec.EmptyProvider) + + companion object { + val Empty = SceneTransitions(transitionSpecs = emptyList()) + } } /** The definition of a transition between [from] and [to]. */ -@Stable -data class TransitionSpec( - val from: SceneKey?, - val to: SceneKey?, - val transformations: List<Transformation>, - val spec: AnimationSpec<Float>, -) { - // TODO(b/302300957): Make sure this cache does not infinitely grow. - private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>() +interface TransitionSpec { + /** + * The scene we are transitioning from. If `null`, this spec can be used to animate from any + * scene. + */ + val from: SceneKey? + + /** + * The scene we are transitioning to. If `null`, this spec can be used to animate from any + * scene. + */ + val to: SceneKey? + + /** + * Return a reversed version of this [TransitionSpec] for a transition going from [to] to + * [from]. + */ + fun reversed(): TransitionSpec + + /* + * The [TransformationSpec] associated to this [TransitionSpec]. + * + * Note that this is called once every a transition associated to this [TransitionSpec] is + * started. + */ + fun transformationSpec(): TransformationSpec +} + +interface TransformationSpec { + /** The [AnimationSpec] used to animate the associated transition progress. */ + val progressSpec: AnimationSpec<Float> + + /** The list of [Transformation] applied to elements during this transition. */ + val transformations: List<Transformation> + + companion object { + internal val Empty = + TransformationSpecImpl(progressSpec = snap(), transformations = emptyList()) + internal val EmptyProvider = { Empty } + } +} - internal fun reverse(): TransitionSpec { - return copy( +internal class TransitionSpecImpl( + override val from: SceneKey?, + override val to: SceneKey?, + private val transformationSpec: () -> TransformationSpecImpl, +) : TransitionSpec { + override fun reversed(): TransitionSpecImpl { + return TransitionSpecImpl( from = to, to = from, - transformations = transformations.fastMap { it.reverse() }, + transformationSpec = { + val reverse = transformationSpec.invoke() + TransformationSpecImpl( + progressSpec = reverse.progressSpec, + transformations = reverse.transformations.map { it.reversed() } + ) + } ) } + override fun transformationSpec(): TransformationSpecImpl = this.transformationSpec.invoke() +} + +/** + * An implementation of [TransformationSpec] that allows the quick retrieval of an element + * [ElementTransformations]. + */ +internal class TransformationSpecImpl( + override val progressSpec: AnimationSpec<Float>, + override val transformations: List<Transformation>, +) : TransformationSpec { + private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>() + internal fun transformations(element: ElementKey, scene: SceneKey): ElementTransformations { return cache .getOrPut(element) { mutableMapOf() } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index 116a66673d0a..0d3bc7d0cd85 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -27,7 +27,8 @@ internal fun Modifier.swipeToScene(gestureHandler: SceneGestureHandler): Modifie fun Scene.shouldEnableSwipes(orientation: Orientation): Boolean = userActions.keys.any { it is Swipe && it.direction.orientation == orientation } - val currentScene = gestureHandler.currentScene + val layoutImpl = gestureHandler.layoutImpl + val currentScene = layoutImpl.scene(layoutImpl.state.transitionState.currentScene) val orientation = gestureHandler.orientation val canSwipe = currentScene.shouldEnableSwipes(orientation) val canOppositeSwipe = diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt index 8f4a36e47212..70468669297c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt @@ -44,7 +44,7 @@ internal fun transitionsImpl( } private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { - val transitionSpecs = mutableListOf<TransitionSpec>() + val transitionSpecs = mutableListOf<TransitionSpecImpl>() override fun to(to: SceneKey, builder: TransitionBuilder.() -> Unit): TransitionSpec { return transition(from = null, to = to, builder) @@ -63,14 +63,15 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { to: SceneKey?, builder: TransitionBuilder.() -> Unit, ): TransitionSpec { - val impl = TransitionBuilderImpl().apply(builder) - val spec = - TransitionSpec( - from, - to, - impl.transformations, - impl.spec, + fun transformationSpec(): TransformationSpecImpl { + val impl = TransitionBuilderImpl().apply(builder) + return TransformationSpecImpl( + progressSpec = impl.spec, + transformations = impl.transformations, ) + } + + val spec = TransitionSpecImpl(from, to, ::transformationSpec) transitionSpecs.add(spec) return spec } @@ -143,7 +144,7 @@ internal class TransitionBuilderImpl : TransitionBuilder { transformations.add( if (reversed) { - transformation.reverse() + transformation.reversed() } else { transformation } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt index 206935558179..0cd11b9914c9 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt @@ -42,7 +42,7 @@ sealed interface Transformation { * Reverse this transformation. This is called when we use Transition(from = A, to = B) when * animating from B to A and there is no Transition(from = B, to = A) defined. */ - fun reverse(): Transformation = this + fun reversed(): Transformation = this } internal class SharedElementTransformation( @@ -77,10 +77,10 @@ internal class RangedPropertyTransformation<T>( val delegate: PropertyTransformation<T>, override val range: TransformationRange, ) : PropertyTransformation<T> by delegate { - override fun reverse(): Transformation { + override fun reversed(): Transformation { return RangedPropertyTransformation( - delegate.reverse() as PropertyTransformation<T>, - range.reverse() + delegate.reversed() as PropertyTransformation<T>, + range.reversed() ) } } @@ -102,7 +102,7 @@ data class TransformationRange( } /** Reverse this range. */ - fun reverse() = TransformationRange(start = reverseBound(end), end = reverseBound(start)) + fun reversed() = TransformationRange(start = reverseBound(end), end = reverseBound(start)) /** Get the progress of this range given the global [transitionProgress]. */ fun progress(transitionProgress: Float): Float { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index e6224df649ca..d9ce5191f3d9 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -55,8 +55,8 @@ class SceneGestureHandlerTest { ) { private var internalCurrentScene: SceneKey by mutableStateOf(SceneA) - private val layoutState: SceneTransitionLayoutState = - SceneTransitionLayoutState(internalCurrentScene) + private val layoutState = + SceneTransitionLayoutStateImpl(internalCurrentScene, EmptyTestTransitions) val mutableUserActionsA: MutableMap<UserAction, SceneKey> = mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC) @@ -93,36 +93,24 @@ class SceneGestureHandlerTest { private val layoutImpl = SceneTransitionLayoutImpl( - onChangeScene = { internalCurrentScene = it }, - builder = scenesBuilder, - transitions = EmptyTestTransitions, state = layoutState, + onChangeScene = { internalCurrentScene = it }, density = Density(1f), edgeDetector = DefaultEdgeDetector, transitionInterceptionThreshold = transitionInterceptionThreshold, + builder = scenesBuilder, coroutineScope = coroutineScope, ) .apply { setScenesTargetSizeForTest(LAYOUT_SIZE) } - val sceneGestureHandler = - SceneGestureHandler( - layoutImpl = layoutImpl, - orientation = Orientation.Vertical, - coroutineScope = coroutineScope, - ) - - val horizontalSceneGestureHandler = - SceneGestureHandler( - layoutImpl = layoutImpl, - orientation = Orientation.Horizontal, - coroutineScope = coroutineScope, - ) - + val sceneGestureHandler = layoutImpl.gestureHandler(Orientation.Vertical) + val horizontalSceneGestureHandler = layoutImpl.gestureHandler(Orientation.Horizontal) val draggable = sceneGestureHandler.draggable fun nestedScrollConnection(nestedScrollBehavior: NestedScrollBehavior) = SceneNestedScrollHandler( - gestureHandler = sceneGestureHandler, + layoutImpl, + orientation = sceneGestureHandler.orientation, topOrLeftBehavior = nestedScrollBehavior, bottomOrRightBehavior = nestedScrollBehavior, ) 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 eeda8d46adfa..c5b8d9ae0d10 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 @@ -40,8 +40,8 @@ class SceneTransitionLayoutStateTest { @Test fun isTransitioningTo_transition() { - val state = SceneTransitionLayoutState(TestScenes.SceneA) - state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneB) + val state = SceneTransitionLayoutStateImpl(TestScenes.SceneA, SceneTransitions.Empty) + state.startTransition(transition(from = TestScenes.SceneA, to = TestScenes.SceneB)) assertThat(state.isTransitioning()).isTrue() assertThat(state.isTransitioning(from = TestScenes.SceneA)).isTrue() diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt index fa94b25028a2..ef729921f4cd 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt @@ -55,7 +55,7 @@ class TransitionDslTest { assertThat(transitions.transitionSpecs) .comparingElementsUsing( - Correspondence.transforming<TransitionSpec, Pair<SceneKey?, SceneKey?>>( + Correspondence.transforming<TransitionSpecImpl, Pair<SceneKey?, SceneKey?>>( { it?.from to it?.to }, "has (from, to) equal to" ) @@ -70,8 +70,8 @@ class TransitionDslTest { @Test fun defaultTransitionSpec() { val transitions = transitions { from(TestScenes.SceneA, to = TestScenes.SceneB) } - val transition = transitions.transitionSpecs.single() - assertThat(transition.spec).isInstanceOf(SpringSpec::class.java) + val transformationSpec = transitions.transitionSpecs.single().transformationSpec() + assertThat(transformationSpec.progressSpec).isInstanceOf(SpringSpec::class.java) } @Test @@ -79,9 +79,9 @@ class TransitionDslTest { val transitions = transitions { from(TestScenes.SceneA, to = TestScenes.SceneB) { spec = tween(durationMillis = 42) } } - val transition = transitions.transitionSpecs.single() - assertThat(transition.spec).isInstanceOf(TweenSpec::class.java) - assertThat((transition.spec as TweenSpec).durationMillis).isEqualTo(42) + val transformationSpec = transitions.transitionSpecs.single().transformationSpec() + assertThat(transformationSpec.progressSpec).isInstanceOf(TweenSpec::class.java) + assertThat((transformationSpec.progressSpec as TweenSpec).durationMillis).isEqualTo(42) } @Test @@ -90,9 +90,10 @@ class TransitionDslTest { from(TestScenes.SceneA, to = TestScenes.SceneB) { fade(TestElements.Foo) } } - val transition = transitions.transitionSpecs.single() - assertThat(transition.transformations.size).isEqualTo(1) - assertThat(transition.transformations.single().range).isEqualTo(null) + val transformations = + transitions.transitionSpecs.single().transformationSpec().transformations + assertThat(transformations.size).isEqualTo(1) + assertThat(transformations.single().range).isEqualTo(null) } @Test @@ -105,8 +106,9 @@ class TransitionDslTest { } } - val transition = transitions.transitionSpecs.single() - assertThat(transition.transformations) + val transformations = + transitions.transitionSpecs.single().transformationSpec().transformations + assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) .containsExactly( TransformationRange(start = 0.1f, end = 0.8f), @@ -127,8 +129,9 @@ class TransitionDslTest { } } - val transition = transitions.transitionSpecs.single() - assertThat(transition.transformations) + val transformations = + transitions.transitionSpecs.single().transformationSpec().transformations + assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) .containsExactly( TransformationRange(start = 100 / 500f, end = 300 / 500f), @@ -149,8 +152,9 @@ class TransitionDslTest { } } - val transition = transitions.transitionSpecs.single() - assertThat(transition.transformations) + val transformations = + transitions.transitionSpecs.single().transformationSpec().transformations + assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) .containsExactly( TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f), @@ -170,9 +174,13 @@ class TransitionDslTest { // Fetch the transition from B to A, which will automatically reverse the transition from A // to B we defined. - val transition = - transitions.transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA) - assertThat(transition.transformations) + val transformations = + transitions + .transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA) + .transformationSpec() + .transformations + + assertThat(transformations) .comparingElementsUsing(TRANSFORMATION_RANGE) .containsExactly( TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f), diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 695d888d94f5..f1701356c134 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -18,7 +18,7 @@ package com.android.keyguard import android.content.res.Configuration -import android.hardware.biometrics.BiometricOverlayConstants +import android.hardware.biometrics.BiometricRequestConstants import android.media.AudioManager import android.telephony.TelephonyManager import android.testing.TestableLooper.RunWithLooper @@ -59,6 +59,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.DevicePostureController import com.android.systemui.statusbar.policy.DeviceProvisionedController @@ -235,6 +236,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { sceneInteractor = sceneInteractor, ) + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) underTest = KeyguardSecurityContainerController( view, @@ -763,16 +765,18 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { @Test fun sideFpsControllerShow() { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) underTest.updateSideFpsVisibility(/* isVisible= */ true) verify(sideFpsController) .show( SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD + BiometricRequestConstants.REASON_AUTH_KEYGUARD ) } @Test fun sideFpsControllerHide() { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) underTest.updateSideFpsVisibility(/* isVisible= */ false) verify(sideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt new file mode 100644 index 000000000000..74f50d8c844b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt @@ -0,0 +1,171 @@ +/* + * 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.accessibility.data.repository + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.settings.FakeSettings +import com.google.common.truth.Truth +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +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) +class ColorCorrectionRepositoryImplTest : SysuiTestCase() { + companion object { + val TEST_USER_1 = UserHandle.of(1)!! + val TEST_USER_2 = UserHandle.of(2)!! + } + + private val testDispatcher = StandardTestDispatcher() + private val scope = TestScope(testDispatcher) + private val settings: FakeSettings = FakeSettings() + + private lateinit var underTest: ColorCorrectionRepository + + @Before + fun setUp() { + underTest = + ColorCorrectionRepositoryImpl( + testDispatcher, + settings, + ) + } + + @Test + fun isEnabled_initiallyGetsSettingsValue() = + scope.runTest { + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + 1, + TEST_USER_1.identifier + ) + + underTest = + ColorCorrectionRepositoryImpl( + testDispatcher, + settings, + ) + + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + runCurrent() + + val actualValue: Boolean = underTest.isEnabled(TEST_USER_1).first() + Truth.assertThat(actualValue).isTrue() + } + + @Test + fun isEnabled_settingUpdated_valueUpdated() = + scope.runTest { + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.DISABLED, + TEST_USER_1.identifier + ) + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.ENABLED, + TEST_USER_1.identifier + ) + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue() + + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.DISABLED, + TEST_USER_1.identifier + ) + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + } + + @Test + fun isEnabled_settingForUserOneOnly_valueUpdatedForUserOneOnly() = + scope.runTest { + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.DISABLED, + TEST_USER_1.identifier + ) + underTest.isEnabled(TEST_USER_2).launchIn(backgroundScope) + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.DISABLED, + TEST_USER_2.identifier + ) + + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + Truth.assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse() + + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.ENABLED, + TEST_USER_1.identifier + ) + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue() + Truth.assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse() + } + + @Test + fun setEnabled() = + scope.runTest { + val success = underTest.setIsEnabled(true, TEST_USER_1) + runCurrent() + Truth.assertThat(success).isTrue() + + val actualValue = + settings.getIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + TEST_USER_1.identifier + ) + Truth.assertThat(actualValue).isEqualTo(ColorCorrectionRepositoryImpl.ENABLED) + } + + @Test + fun setDisabled() = + scope.runTest { + val success = underTest.setIsEnabled(false, TEST_USER_1) + runCurrent() + Truth.assertThat(success).isTrue() + + val actualValue = + settings.getIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + TEST_USER_1.identifier + ) + Truth.assertThat(actualValue).isEqualTo(ColorCorrectionRepositoryImpl.DISABLED) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index a1b801cd3d3f..f8321b7e7eb3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -22,9 +22,9 @@ import android.app.ActivityTaskManager import android.content.ComponentName import android.graphics.Insets import android.graphics.Rect -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS -import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS +import android.hardware.biometrics.BiometricRequestConstants.REASON_UNKNOWN import android.hardware.biometrics.SensorLocationInternal import android.hardware.biometrics.SensorProperties import android.hardware.display.DisplayManager @@ -65,6 +65,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock @@ -144,6 +145,7 @@ class SideFpsControllerTest : SysuiTestCase() { @Before fun setup() { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) displayRepository = FakeDisplayRepository() displayStateRepository = FakeDisplayStateRepository() keyguardBouncerRepository = FakeKeyguardBouncerRepository() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 90d36e7fd814..a726b7c2b075 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -17,12 +17,12 @@ package com.android.systemui.biometrics import android.graphics.Rect -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS -import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING -import android.hardware.biometrics.BiometricOverlayConstants.ShowReason +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING +import android.hardware.biometrics.BiometricRequestConstants.RequestReason import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater @@ -135,7 +135,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } private fun withReason( - @ShowReason reason: Int, + @RequestReason reason: Int, isDebuggable: Boolean = false, enableDeviceEntryUdfpsRefactor: Boolean = false, block: () -> Unit, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 97ee526b7108..dddcf18c1ede 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -43,7 +43,7 @@ import static org.mockito.Mockito.when; import android.graphics.Rect; import android.hardware.biometrics.BiometricFingerprintConstants; -import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.SensorProperties; import android.hardware.display.DisplayManager; @@ -359,7 +359,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void dozeTimeTick() throws RemoteException { mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); mUdfpsController.dozeTimeTick(); verify(mUdfpsView).dozeTimeTick(); @@ -455,7 +455,7 @@ public class UdfpsControllerTest extends SysuiTestCase { public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException { // GIVEN overlay was showing and the udfps bouncer is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); // WHEN the overlay is hidden @@ -469,7 +469,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void showUdfpsOverlay_callsListener() throws RemoteException { mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); verify(mFingerprintManager).onUdfpsUiEvent(FingerprintManager.UDFPS_UI_OVERLAY_SHOWN, @@ -479,7 +479,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception { mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong()); @@ -520,7 +520,7 @@ public class UdfpsControllerTest extends SysuiTestCase { reset(mWindowManager); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_ENROLL_ENROLLING, + BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); verify(mWindowManager).addView(any(), any()); @@ -555,7 +555,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // Show the overlay. mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); verify(mWindowManager).addView(any(), any()); @@ -637,7 +637,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // Show the overlay. mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); @@ -720,7 +720,7 @@ public class UdfpsControllerTest extends SysuiTestCase { initUdfpsController(testParams.sensorProps); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_DOWN is received @@ -778,7 +778,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN that the overlay is showing and screen is on and fp is running mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); // WHEN fingerprint is requested because of AOD interrupt @@ -808,7 +808,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN AOD interrupt mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); @@ -886,7 +886,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN overlay is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { when(mUdfpsView.isDisplayConfigured()).thenReturn(true); @@ -917,7 +917,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN AOD interrupt mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); @@ -958,7 +958,7 @@ public class UdfpsControllerTest extends SysuiTestCase { final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { // Configure UdfpsView to accept the ACTION_UP event @@ -1019,7 +1019,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN screen off mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOff(); mFgExecutor.runAllReady(); @@ -1041,7 +1041,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN showing overlay mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); @@ -1126,7 +1126,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN that the overlay is showing and a11y touch exploration NOT enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); if (a11y) { @@ -1148,7 +1148,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN that the overlay is showing and a11y touch exploration NOT enabled when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); @@ -1197,7 +1197,7 @@ public class UdfpsControllerTest extends SysuiTestCase { -1 /* pointerId */, touchData); mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); @@ -1289,7 +1289,7 @@ public class UdfpsControllerTest extends SysuiTestCase { public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException { // GIVEN UDFPS overlay is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // GIVEN there's been an AoD interrupt @@ -1316,7 +1316,7 @@ public class UdfpsControllerTest extends SysuiTestCase { throws RemoteException { // GIVEN UDFPS overlay is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // GIVEN there's been an AoD interrupt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt index 90e0c19b7c65..a3bf3f492e6e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.utils.os.FakeHandler @@ -105,8 +106,10 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() { job.cancel() } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun shouldUpdateSideFps_show() = runTest { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) var count = 0 val job = underTest.shouldUpdateSideFps.onEach { count++ }.launchIn(this) repository.setPrimaryShow(true) @@ -116,8 +119,10 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() { job.cancel() } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun shouldUpdateSideFps_hide() = runTest { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) repository.setPrimaryShow(true) var count = 0 val job = underTest.shouldUpdateSideFps.onEach { count++ }.launchIn(this) @@ -128,8 +133,10 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() { job.cancel() } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun sideFpsShowing() = runTest { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) var sideFpsIsShowing = false val job = underTest.sideFpsShowing.onEach { sideFpsIsShowing = it }.launchIn(this) repository.setSideFpsShowing(true) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt index 2b7221ec192c..6b7d2635ffa4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt @@ -304,4 +304,34 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() { ) assertThat(authenticationStatus).isNull() } + + @Test + fun onBiometricRunningStateChanged_shouldUpdateIndicatorVisibility() = + testScope.runTest { + val shouldUpdateIndicatorVisibility by + collectLastValue(underTest.shouldUpdateIndicatorVisibility) + runCurrent() + + verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) + assertThat(shouldUpdateIndicatorVisibility).isFalse() + + invokeOnCallback { + it.onBiometricRunningStateChanged(false, BiometricSourceType.FINGERPRINT) + } + assertThat(shouldUpdateIndicatorVisibility).isTrue() + } + + @Test + fun onStrongAuthStateChanged_shouldUpdateIndicatorVisibility() = + testScope.runTest { + val shouldUpdateIndicatorVisibility by + collectLastValue(underTest.shouldUpdateIndicatorVisibility) + runCurrent() + + verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture()) + assertThat(shouldUpdateIndicatorVisibility).isFalse() + + invokeOnCallback { it.onStrongAuthStateChanged(0) } + assertThat(shouldUpdateIndicatorVisibility).isTrue() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt new file mode 100644 index 000000000000..8ee6d2005350 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt @@ -0,0 +1,91 @@ +/* + * 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.qs.tiles.impl.colorcorrection.domain + +import android.graphics.drawable.TestStubDrawable +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.impl.colorcorrection.qsColorCorrectionTileConfig +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorCorrectionTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val colorCorrectionTileConfig = kosmos.qsColorCorrectionTileConfig + private val subtitleArray = + context.resources.getStringArray(R.array.tile_states_color_correction) + // Using lazy (versus =) to make sure we override the right context -- see b/311612168 + private val mapper by lazy { + ColorCorrectionTileMapper( + context.orCreateTestableResources + .apply { addOverride(R.drawable.ic_qs_color_correction, TestStubDrawable()) } + .resources, + context.theme + ) + } + + @Test + fun disabledModel() { + val inputModel = ColorCorrectionTileModel(false) + + val outputState = mapper.map(colorCorrectionTileConfig, inputModel) + + val expectedState = + createColorCorrectionTileState(QSTileState.ActivationState.INACTIVE, subtitleArray[1]) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun enabledModel() { + val inputModel = ColorCorrectionTileModel(true) + + val outputState = mapper.map(colorCorrectionTileConfig, inputModel) + + val expectedState = + createColorCorrectionTileState(QSTileState.ActivationState.ACTIVE, subtitleArray[2]) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + private fun createColorCorrectionTileState( + activationState: QSTileState.ActivationState, + secondaryLabel: String + ): QSTileState { + val label = context.getString(R.string.quick_settings_color_correction_label) + return QSTileState( + { Icon.Loaded(context.getDrawable(R.drawable.ic_qs_color_correction)!!, null) }, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + label, + null, + QSTileState.SideViewIcon.None, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt new file mode 100644 index 000000000000..8c612acad887 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt @@ -0,0 +1,72 @@ +/* + * 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.qs.tiles.impl.colorcorrection.domain.interactor + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository +import com.android.systemui.coroutines.collectValues +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toCollection +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorCorrectionTileDataInteractorTest : SysuiTestCase() { + + private val colorCorrectionRepository = FakeColorCorrectionRepository() + private val underTest: ColorCorrectionTileDataInteractor = + ColorCorrectionTileDataInteractor(colorCorrectionRepository) + + @Test + fun alwaysAvailable() = runTest { + val availability = underTest.availability(TEST_USER).toCollection(mutableListOf()) + + assertThat(availability).hasSize(1) + assertThat(availability.last()).isTrue() + } + + @Test + fun dataMatchesTheRepository() = runTest { + val dataList: List<ColorCorrectionTileModel> by + collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))) + runCurrent() + + colorCorrectionRepository.setIsEnabled(true, TEST_USER) + runCurrent() + + colorCorrectionRepository.setIsEnabled(false, TEST_USER) + runCurrent() + + assertThat(dataList).hasSize(3) + assertThat(dataList.map { it.isEnabled }).isEqualTo(listOf(false, true, false)) + } + + private companion object { + val TEST_USER = UserHandle.of(1)!! + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..3049cc079a1c --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt @@ -0,0 +1,89 @@ +/* + * 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.qs.tiles.impl.colorcorrection.domain.interactor + +import android.os.UserHandle +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorCorrectionTileUserActionInteractorTest : SysuiTestCase() { + + private val testUser = UserHandle.CURRENT + private val repository = FakeColorCorrectionRepository() + private val inputHandler = FakeQSTileIntentUserInputHandler() + + private val underTest = + ColorCorrectionUserActionInteractor( + repository, + inputHandler, + ) + + @Test + fun handleClickWhenEnabled() = runTest { + val wasEnabled = true + repository.setIsEnabled(wasEnabled, testUser) + + underTest.handleInput(QSTileInputTestKtx.click(ColorCorrectionTileModel(wasEnabled))) + + assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled) + } + + @Test + fun handleClickWhenDisabled() = runTest { + val wasEnabled = false + repository.setIsEnabled(wasEnabled, testUser) + + underTest.handleInput(QSTileInputTestKtx.click(ColorCorrectionTileModel(wasEnabled))) + + assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled) + } + + @Test + fun handleLongClickWhenDisabled() = runTest { + val enabled = false + + underTest.handleInput(QSTileInputTestKtx.longClick(ColorCorrectionTileModel(enabled))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + } + } + + @Test + fun handleLongClickWhenEnabled() = runTest { + val enabled = true + + underTest.handleInput(QSTileInputTestKtx.longClick(ColorCorrectionTileModel(enabled))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt index 4a221134ce67..2eeb75e3443b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt @@ -16,23 +16,27 @@ package com.android.systemui.qs.tiles.impl.custom +import android.annotation.SuppressLint +import android.content.BroadcastReceiver import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.qs.external.TileLifecycleManager -import com.android.systemui.qs.external.TileServiceManager +import com.android.systemui.coroutines.collectValues import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl -import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository -import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository.DefaultsRequest +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.mockito.nullable import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -49,14 +53,12 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) +@SuppressLint("UnspecifiedRegisterReceiverFlag") // Not needed in the test class CustomTilePackageUpdatesRepositoryTest : SysuiTestCase() { - @Mock private lateinit var tileServiceManager: TileServiceManager + @Mock private lateinit var mockedContext: Context + @Captor private lateinit var listenerCaptor: ArgumentCaptor<BroadcastReceiver> - @Captor - private lateinit var listenerCaptor: ArgumentCaptor<TileLifecycleManager.TileChangeListener> - - private val defaultsRepository = FakeCustomTileDefaultsRepository() private val testDispatcher = StandardTestDispatcher() private val testScope = TestScope(testDispatcher) @@ -69,37 +71,29 @@ class CustomTilePackageUpdatesRepositoryTest : SysuiTestCase() { underTest = CustomTilePackageUpdatesRepositoryImpl( TileSpec.create(COMPONENT_1), - USER, - tileServiceManager, - defaultsRepository, + mockedContext, testScope.backgroundScope, + testDispatcher, ) } @Test - fun packageChangesUpdatesDefaults() = + fun packageChangesEmittedForTilePassing() = testScope.runTest { - val events = mutableListOf<Unit>() - underTest.packageChanges.onEach { events.add(it) }.launchIn(backgroundScope) + val events by collectValues(underTest.getPackageChangesForUser(USER_1)) runCurrent() - verify(tileServiceManager).setTileChangeListener(capture(listenerCaptor)) - emitPackageChange() + emitPackageChange(COMPONENT_1) runCurrent() assertThat(events).hasSize(1) - assertThat(defaultsRepository.defaultsRequests).isNotEmpty() - assertThat(defaultsRepository.defaultsRequests.last()) - .isEqualTo(DefaultsRequest(USER, COMPONENT_1, true)) } @Test - fun packageChangesEmittedOnlyForTheTile() = + fun packageChangesEmittedForAnotherTileIgnored() = testScope.runTest { - val events = mutableListOf<Unit>() - underTest.packageChanges.onEach { events.add(it) }.launchIn(backgroundScope) + val events by collectValues(underTest.getPackageChangesForUser(USER_1)) runCurrent() - verify(tileServiceManager).setTileChangeListener(capture(listenerCaptor)) emitPackageChange(COMPONENT_2) runCurrent() @@ -107,12 +101,60 @@ class CustomTilePackageUpdatesRepositoryTest : SysuiTestCase() { assertThat(events).isEmpty() } - private fun emitPackageChange(componentName: ComponentName = COMPONENT_1) { - listenerCaptor.value.onTileChanged(componentName) + @Test + fun unsupportedActionDoesntEmmit() = + testScope.runTest { + val events by collectValues(underTest.getPackageChangesForUser(USER_1)) + runCurrent() + + verify(mockedContext) + .registerReceiverAsUser( + capture(listenerCaptor), + any(), + any(), + nullable(), + nullable() + ) + listenerCaptor.value.onReceive(mockedContext, Intent(Intent.ACTION_MAIN)) + runCurrent() + + assertThat(events).isEmpty() + } + + @Test + fun cachesCallsPerUser() = + testScope.runTest { + underTest.getPackageChangesForUser(USER_1).launchIn(backgroundScope) + underTest.getPackageChangesForUser(USER_1).launchIn(backgroundScope) + underTest.getPackageChangesForUser(USER_2).launchIn(backgroundScope) + underTest.getPackageChangesForUser(USER_2).launchIn(backgroundScope) + runCurrent() + + // Register receiver once per each user + verify(mockedContext) + .registerReceiverAsUser(any(), eq(USER_1), any(), nullable(), nullable()) + verify(mockedContext) + .registerReceiverAsUser(any(), eq(USER_2), any(), nullable(), nullable()) + } + + private fun emitPackageChange(componentName: ComponentName) { + verify(mockedContext) + .registerReceiverAsUser(capture(listenerCaptor), any(), any(), nullable(), nullable()) + listenerCaptor.value.onReceive( + mockedContext, + Intent(Intent.ACTION_PACKAGE_CHANGED).apply { + type = IntentFilter.SCHEME_PACKAGE + putExtra( + Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, + arrayOf(componentName.packageName) + ) + } + ) } private companion object { - val USER = UserHandle(0) + val USER_1 = UserHandle(1) + val USER_2 = UserHandle(2) val COMPONENT_1 = ComponentName("pkg.test.1", "cls.test") val COMPONENT_2 = ComponentName("pkg.test.2", "cls.test") } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml b/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml index 48769fdebd99..073f090027ba 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml @@ -18,8 +18,6 @@ <TextView android:id="@+id/digit_text" style="@style/Widget.TextView.NumPadKey.Digit" - android:autoSizeMaxTextSize="32sp" - android:autoSizeTextType="uniform" android:layout_width="wrap_content" android:layout_height="wrap_content" /> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 88f7bcd5d907..2cca9510417a 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -69,7 +69,7 @@ <item name="android:singleLine">true</item> <item name="android:gravity">center_horizontal|center_vertical</item> <item name="android:background">@null</item> - <item name="android:textSize">32sp</item> + <item name="android:textSize">32dp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> <item name="android:paddingBottom">-16dp</item> diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml index 4d952209da6f..fc4bf8a65643 100644 --- a/packages/SystemUI/res/layout/sidefps_view.xml +++ b/packages/SystemUI/res/layout/sidefps_view.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.SideFpsLottieViewWrapper +<com.android.systemui.biometrics.SideFpsIndicatorView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/sidefps_animation" diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt index 80f70a0cd2f2..30648291366a 100644 --- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt +++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt @@ -37,6 +37,10 @@ data class BiometricModalities( val hasSfps: Boolean get() = hasFingerprint && fingerprintProperties!!.isAnySidefpsType + /** If UDFPS authentication is available. */ + val hasUdfps: Boolean + get() = hasFingerprint && fingerprintProperties!!.isAnyUdfpsType + /** If fingerprint authentication is available (and [faceProperties] is non-null). */ val hasFace: Boolean get() = faceProperties != null diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 5c206e95966b..5d63c2a92ba8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -281,7 +281,10 @@ public class KeyguardPatternView extends KeyguardInputView setAlpha(1f); mAppearAnimationUtils.startAnimation2d( mLockPatternView.getCellStates(), - () -> enableClipping(true), + () -> { + enableClipping(true); + mLockPatternView.invalidate(); + }, KeyguardPatternView.this); }); if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 0a4378e07b45..cce2018f733f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -38,7 +38,7 @@ import android.app.admin.DevicePolicyManager; import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Resources; -import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.BiometricRequestConstants; import android.media.AudioManager; import android.metrics.LogMaker; import android.os.SystemClock; @@ -74,6 +74,7 @@ import com.android.systemui.Gefingerpoken; import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate; import com.android.systemui.biometrics.SideFpsController; import com.android.systemui.biometrics.SideFpsUiRequestSource; +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor; import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.classifier.FalsingA11yDelegate; @@ -486,7 +487,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mSceneContainerFlags = sceneContainerFlags; mGlobalSettings = globalSettings; mSessionTracker = sessionTracker; - mSideFpsController = sideFpsController; + if (SideFpsControllerRefactor.isEnabled()) { + mSideFpsController = Optional.empty(); + } else { + mSideFpsController = sideFpsController; + } mFalsingA11yDelegate = falsingA11yDelegate; mTelephonyManager = telephonyManager; mViewMediatorCallback = viewMediatorCallback; @@ -569,12 +574,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mView.clearFocus(); } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR /** * Shows and hides the side finger print sensor animation. * * @param isVisible sets whether we show or hide the side fps animation */ public void updateSideFpsVisibility(boolean isVisible) { + SideFpsControllerRefactor.assertInLegacyMode(); if (!mSideFpsController.isPresent()) { return; } @@ -582,7 +589,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard if (isVisible) { mSideFpsController.get().show( SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD + BiometricRequestConstants.REASON_AUTH_KEYGUARD ); } else { mSideFpsController.get().hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt index ca859de73a36..24aa11e10f30 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * 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. @@ -16,61 +16,16 @@ package com.android.systemui.accessibility -import com.android.systemui.qs.tileimpl.QSTileImpl -import com.android.systemui.qs.tiles.ColorCorrectionTile -import com.android.systemui.qs.tiles.ColorInversionTile -import com.android.systemui.qs.tiles.DreamTile -import com.android.systemui.qs.tiles.FontScalingTile -import com.android.systemui.qs.tiles.NightDisplayTile -import com.android.systemui.qs.tiles.OneHandedModeTile -import com.android.systemui.qs.tiles.ReduceBrightColorsTile +import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository +import com.android.systemui.accessibility.data.repository.ColorCorrectionRepositoryImpl +import com.android.systemui.accessibility.qs.QSAccessibilityModule import dagger.Binds import dagger.Module -import dagger.multibindings.IntoMap -import dagger.multibindings.StringKey -@Module +@Module(includes = [QSAccessibilityModule::class]) interface AccessibilityModule { - - /** Inject ColorInversionTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(ColorInversionTile.TILE_SPEC) - fun bindColorInversionTile(colorInversionTile: ColorInversionTile): QSTileImpl<*> - - /** Inject NightDisplayTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(NightDisplayTile.TILE_SPEC) - fun bindNightDisplayTile(nightDisplayTile: NightDisplayTile): QSTileImpl<*> - - /** Inject ReduceBrightColorsTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(ReduceBrightColorsTile.TILE_SPEC) - fun bindReduceBrightColorsTile(reduceBrightColorsTile: ReduceBrightColorsTile): QSTileImpl<*> - - /** Inject OneHandedModeTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(OneHandedModeTile.TILE_SPEC) - fun bindOneHandedModeTile(oneHandedModeTile: OneHandedModeTile): QSTileImpl<*> - - /** Inject ColorCorrectionTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(ColorCorrectionTile.TILE_SPEC) - fun bindColorCorrectionTile(colorCorrectionTile: ColorCorrectionTile): QSTileImpl<*> - - /** Inject DreamTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(DreamTile.TILE_SPEC) - fun bindDreamTile(dreamTile: DreamTile): QSTileImpl<*> - - /** Inject FontScalingTile into tileMap in QSModule */ @Binds - @IntoMap - @StringKey(FontScalingTile.TILE_SPEC) - fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*> + abstract fun colorCorrectionRepository( + impl: ColorCorrectionRepositoryImpl + ): ColorCorrectionRepository } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt new file mode 100644 index 000000000000..6483ae44d5ec --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt @@ -0,0 +1,73 @@ +/* + * 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.accessibility.data.repository + +import android.os.UserHandle +import android.provider.Settings.Secure +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext + +/** Provides data related to color correction. */ +interface ColorCorrectionRepository { + /** Observable for whether color correction is enabled */ + fun isEnabled(userHandle: UserHandle): Flow<Boolean> + + /** Sets color correction enabled state. */ + suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean +} + +@SysUISingleton +class ColorCorrectionRepositoryImpl +@Inject +constructor( + @Background private val bgCoroutineContext: CoroutineContext, + private val secureSettings: SecureSettings, +) : ColorCorrectionRepository { + + companion object { + const val SETTING_NAME = Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED + const val DISABLED = 0 + const val ENABLED = 1 + } + + override fun isEnabled(userHandle: UserHandle): Flow<Boolean> = + secureSettings + .observerFlow(userHandle.identifier, SETTING_NAME) + .onStart { emit(Unit) } + .map { secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED } + .distinctUntilChanged() + .flowOn(bgCoroutineContext) + + override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean = + withContext(bgCoroutineContext) { + secureSettings.putIntForUser( + SETTING_NAME, + if (isEnabled) ENABLED else DISABLED, + userHandle.identifier + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt new file mode 100644 index 000000000000..df7fdb8e6058 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt @@ -0,0 +1,124 @@ +/* + * 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.accessibility.qs + +import com.android.systemui.qs.QsEventLogger +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.qs.tiles.ColorCorrectionTile +import com.android.systemui.qs.tiles.ColorInversionTile +import com.android.systemui.qs.tiles.DreamTile +import com.android.systemui.qs.tiles.FontScalingTile +import com.android.systemui.qs.tiles.NightDisplayTile +import com.android.systemui.qs.tiles.OneHandedModeTile +import com.android.systemui.qs.tiles.ReduceBrightColorsTile +import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.ColorCorrectionTileMapper +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionUserActionInteractor +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel +import com.android.systemui.res.R +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey + +@Module +interface QSAccessibilityModule { + + /** Inject ColorInversionTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(ColorInversionTile.TILE_SPEC) + fun bindColorInversionTile(colorInversionTile: ColorInversionTile): QSTileImpl<*> + + /** Inject NightDisplayTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(NightDisplayTile.TILE_SPEC) + fun bindNightDisplayTile(nightDisplayTile: NightDisplayTile): QSTileImpl<*> + + /** Inject ReduceBrightColorsTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(ReduceBrightColorsTile.TILE_SPEC) + fun bindReduceBrightColorsTile(reduceBrightColorsTile: ReduceBrightColorsTile): QSTileImpl<*> + + /** Inject OneHandedModeTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(OneHandedModeTile.TILE_SPEC) + fun bindOneHandedModeTile(oneHandedModeTile: OneHandedModeTile): QSTileImpl<*> + + /** Inject ColorCorrectionTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(ColorCorrectionTile.TILE_SPEC) + fun bindColorCorrectionTile(colorCorrectionTile: ColorCorrectionTile): QSTileImpl<*> + + /** Inject DreamTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(DreamTile.TILE_SPEC) + fun bindDreamTile(dreamTile: DreamTile): QSTileImpl<*> + + /** Inject FontScalingTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(FontScalingTile.TILE_SPEC) + fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*> + + companion object { + + const val COLOR_CORRECTION_TILE_SPEC = "color_correction" + + @Provides + @IntoMap + @StringKey(COLOR_CORRECTION_TILE_SPEC) + fun provideColorCorrectionTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = + QSTileConfig( + tileSpec = TileSpec.create(COLOR_CORRECTION_TILE_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = R.drawable.ic_qs_color_correction, + labelRes = R.string.quick_settings_color_correction_label, + ), + instanceId = uiEventLogger.getNewInstanceId(), + ) + + /** Inject ColorCorrectionTile into tileViewModelMap in QSModule */ + @Provides + @IntoMap + @StringKey(COLOR_CORRECTION_TILE_SPEC) + fun provideColorCorrectionTileViewModel( + factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>, + mapper: ColorCorrectionTileMapper, + stateInteractor: ColorCorrectionTileDataInteractor, + userActionInteractor: ColorCorrectionUserActionInteractor + ): QSTileViewModel = + factory.create( + TileSpec.create(COLOR_CORRECTION_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 877afce7fe65..5fba761b2f09 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -70,6 +70,7 @@ import com.android.systemui.CoreStartable; import com.android.systemui.biometrics.domain.interactor.LogContextInteractor; import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor; +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; @@ -88,6 +89,8 @@ import com.android.systemui.util.concurrency.Execution; import dagger.Lazy; +import kotlin.Unit; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -101,7 +104,6 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; -import kotlin.Unit; import kotlinx.coroutines.CoroutineScope; /** @@ -317,7 +319,9 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null; if (mSidefpsProps != null) { - mSideFpsController = mSidefpsControllerFactory.get(); + if (!SideFpsControllerRefactor.isEnabled()) { + mSideFpsController = mSidefpsControllerFactory.get(); + } } mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() { @@ -1194,7 +1198,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, * Whether the passed userId has enrolled SFPS. */ public boolean isSfpsEnrolled(int userId) { - if (mSideFpsController == null) { + if (mSidefpsProps == null) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 91cee9e51a93..ac99fc69b2b5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -23,9 +23,9 @@ import android.graphics.PixelFormat import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter import android.graphics.Rect -import android.hardware.biometrics.BiometricOverlayConstants -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS +import android.hardware.biometrics.BiometricRequestConstants +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS import android.hardware.biometrics.SensorLocationInternal import android.hardware.display.DisplayManager import android.hardware.fingerprint.FingerprintManager @@ -58,6 +58,7 @@ import com.android.internal.annotations.VisibleForTesting import com.android.keyguard.KeyguardPINView import com.android.systemui.Dumpable import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -91,7 +92,7 @@ constructor( @Main private val mainExecutor: DelayableExecutor, @Main private val handler: Handler, private val alternateBouncerInteractor: AlternateBouncerInteractor, - @Application private val scope: CoroutineScope, + @Application private val applicationScope: CoroutineScope, dumpManager: DumpManager, fpsUnlockTracker: FpsUnlockTracker ) : Dumpable { @@ -110,7 +111,7 @@ constructor( handler, sensorProps, { reason -> onOrientationChanged(reason) }, - BiometricOverlayConstants.REASON_UNKNOWN + BiometricRequestConstants.REASON_UNKNOWN ) @VisibleForTesting val orientationListener = orientationReasonListener.orientationListener @@ -169,25 +170,27 @@ constructor( } init { - fpsUnlockTracker.startTracking() - fingerprintManager?.setSidefpsController( - object : ISidefpsController.Stub() { - override fun show( - sensorId: Int, - @BiometricOverlayConstants.ShowReason reason: Int - ) = - if (reason.isReasonToAutoShow(activityTaskManager)) { - show(SideFpsUiRequestSource.AUTO_SHOW, reason) - } else { - hide(SideFpsUiRequestSource.AUTO_SHOW) - } - - override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW) - } - ) - listenForAlternateBouncerVisibility() + if (!SideFpsControllerRefactor.isEnabled) { + fpsUnlockTracker.startTracking() + fingerprintManager?.setSidefpsController( + object : ISidefpsController.Stub() { + override fun show( + sensorId: Int, + @BiometricRequestConstants.RequestReason reason: Int + ) = + if (reason.isReasonToAutoShow(activityTaskManager)) { + show(SideFpsUiRequestSource.AUTO_SHOW, reason) + } else { + hide(SideFpsUiRequestSource.AUTO_SHOW) + } + + override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW) + } + ) + listenForAlternateBouncerVisibility() - dumpManager.registerDumpable(this) + dumpManager.registerDumpable(this) + } } private fun listenForAlternateBouncerVisibility() { @@ -195,7 +198,7 @@ constructor( alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, "SideFpsController") } - scope.launch { + applicationScope.launch { alternateBouncerInteractor.isVisible.collect { isVisible: Boolean -> if (isVisible) { show(SideFpsUiRequestSource.ALTERNATE_BOUNCER, REASON_AUTH_KEYGUARD) @@ -209,8 +212,10 @@ constructor( /** Shows the side fps overlay if not already shown. */ fun show( request: SideFpsUiRequestSource, - @BiometricOverlayConstants.ShowReason reason: Int = BiometricOverlayConstants.REASON_UNKNOWN + @BiometricRequestConstants.RequestReason + reason: Int = BiometricRequestConstants.REASON_UNKNOWN ) { + SideFpsControllerRefactor.assertInLegacyMode() if (!displayStateInteractor.isInRearDisplayMode.value) { requests.add(request) mainExecutor.execute { @@ -229,6 +234,7 @@ constructor( /** Hides the fps overlay if shown. */ fun hide(request: SideFpsUiRequestSource) { + SideFpsControllerRefactor.assertInLegacyMode() requests.remove(request) mainExecutor.execute { if (requests.isEmpty()) { @@ -239,6 +245,7 @@ constructor( /** Hide the arrow indicator. */ fun hideIndicator() { + SideFpsControllerRefactor.assertInLegacyMode() val lottieAnimationView = overlayView?.findViewById(R.id.sidefps_animation) as LottieAnimationView? lottieAnimationView?.visibility = INVISIBLE @@ -246,6 +253,7 @@ constructor( /** Show the arrow indicator. */ fun showIndicator() { + SideFpsControllerRefactor.assertInLegacyMode() val lottieAnimationView = overlayView?.findViewById(R.id.sidefps_animation) as LottieAnimationView? lottieAnimationView?.visibility = VISIBLE @@ -279,13 +287,13 @@ constructor( pw.println("currentRotation=${displayInfo.rotation}") } - private fun onOrientationChanged(@BiometricOverlayConstants.ShowReason reason: Int) { + private fun onOrientationChanged(@BiometricRequestConstants.RequestReason reason: Int) { if (overlayView != null) { createOverlayForDisplay(reason) } } - private fun createOverlayForDisplay(@BiometricOverlayConstants.ShowReason reason: Int) { + private fun createOverlayForDisplay(@BiometricRequestConstants.RequestReason reason: Int) { val view = layoutInflater.inflate(R.layout.sidefps_view, null, false) overlayView = view val display = context.display!! @@ -395,7 +403,7 @@ private val FingerprintManager?.sideFpsSensorProperties: FingerprintSensorProper /** Returns [True] when the device has a side fingerprint sensor. */ fun FingerprintManager?.hasSideFpsSensor(): Boolean = this?.sideFpsSensorProperties != null -@BiometricOverlayConstants.ShowReason +@BiometricRequestConstants.RequestReason private fun Int.isReasonToAutoShow(activityTaskManager: ActivityTaskManager): Boolean = when (this) { REASON_AUTH_KEYGUARD -> false @@ -434,7 +442,7 @@ private fun Display.isNaturalOrientation(): Boolean = private fun LottieAnimationView.addOverlayDynamicColor( context: Context, - @BiometricOverlayConstants.ShowReason reason: Int + @BiometricRequestConstants.RequestReason reason: Int ) { fun update() { val isKeyguard = reason == REASON_AUTH_KEYGUARD @@ -501,7 +509,7 @@ class OrientationReasonListener( handler: Handler, sensorProps: FingerprintSensorPropertiesInternal, onOrientationChanged: (reason: Int) -> Unit, - @BiometricOverlayConstants.ShowReason var reason: Int + @BiometricRequestConstants.RequestReason var reason: Int ) { val orientationListener = BiometricDisplayListener( @@ -516,7 +524,7 @@ class OrientationReasonListener( /** * The source of a request to show the side fps visual indicator. This is distinct from - * [BiometricOverlayConstants] which corrresponds with the reason fingerprint authentication is + * [BiometricRequestConstants] which corresponds with the reason fingerprint authentication is * requested. */ enum class SideFpsUiRequestSource { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsIndicatorView.kt index e98f6db12d34..d5e25ac84aba 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsIndicatorView.kt @@ -19,6 +19,6 @@ import android.content.Context import android.util.AttributeSet import com.android.systemui.util.wrapper.LottieViewWrapper -class SideFpsLottieViewWrapper +class SideFpsIndicatorView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index bb6ef41bdfd3..65668b56a9f3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -19,10 +19,10 @@ package com.android.systemui.biometrics; import static android.app.StatusBarManager.SESSION_BIOMETRIC_PROMPT; import static android.app.StatusBarManager.SESSION_KEYGUARD; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; -import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP; -import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD; -import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING; -import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR; +import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP; +import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD; +import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING; +import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR; import static com.android.internal.util.LatencyTracker.ACTION_UDFPS_ILLUMINATE; import static com.android.internal.util.Preconditions.checkNotNull; @@ -106,6 +106,8 @@ import com.android.systemui.util.time.SystemClock; import dagger.Lazy; +import kotlin.Unit; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; @@ -115,8 +117,6 @@ import java.util.concurrent.Executor; import javax.inject.Inject; import javax.inject.Provider; -import kotlin.Unit; - import kotlinx.coroutines.ExperimentalCoroutinesApi; /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 452cd6a7c4df..dae6d08f7331 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -21,13 +21,13 @@ import android.annotation.UiThread import android.content.Context import android.graphics.PixelFormat import android.graphics.Rect -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS -import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING -import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR -import android.hardware.biometrics.BiometricOverlayConstants.ShowReason +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR +import android.hardware.biometrics.BiometricRequestConstants.RequestReason import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.os.Build import android.os.RemoteException @@ -96,7 +96,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider, val requestId: Long, - @ShowReason val requestReason: Int, + @RequestReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, private val onTouch: (View, MotionEvent, Boolean) -> Boolean, private val activityLaunchAnimator: ActivityLaunchAnimator, @@ -461,7 +461,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( } } -@ShowReason +@RequestReason private fun Int.isImportantForAccessibility() = this == REASON_ENROLL_FIND_SENSOR || this == REASON_ENROLL_ENROLLING || diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt index e3dbcb523258..88b9e1bdfd97 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt @@ -18,13 +18,13 @@ package com.android.systemui.biometrics import android.content.Context import android.graphics.Rect -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS -import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING -import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR -import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR +import android.hardware.biometrics.BiometricRequestConstants.REASON_UNKNOWN import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.util.Log import android.view.LayoutInflater diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt index 72fcfe777e79..8ae6f87f4f83 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt @@ -18,8 +18,11 @@ package com.android.systemui.biometrics.dagger import android.content.res.Resources import com.android.internal.R +import com.android.systemui.CoreStartable import com.android.systemui.biometrics.EllipseOverlapDetectorParams import com.android.systemui.biometrics.UdfpsUtils +import com.android.systemui.biometrics.data.repository.BiometricStatusRepository +import com.android.systemui.biometrics.data.repository.BiometricStatusRepositoryImpl import com.android.systemui.biometrics.data.repository.DisplayStateRepository import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl import com.android.systemui.biometrics.data.repository.FacePropertyRepository @@ -33,11 +36,14 @@ import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector import com.android.systemui.biometrics.udfps.EllipseOverlapDetector import com.android.systemui.biometrics.udfps.OverlapDetector +import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.concurrency.ThreadFactory import dagger.Binds import dagger.Module import dagger.Provides +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap import java.util.concurrent.Executor import javax.inject.Qualifier @@ -46,6 +52,11 @@ import javax.inject.Qualifier interface BiometricsModule { @Binds + @IntoMap + @ClassKey(SideFpsOverlayViewBinder::class) + fun bindsSideFpsOverlayViewBinder(viewBinder: SideFpsOverlayViewBinder): CoreStartable + + @Binds @SysUISingleton fun faceSettings(impl: FaceSettingsRepositoryImpl): FaceSettingsRepository @@ -57,6 +68,10 @@ interface BiometricsModule { @Binds @SysUISingleton + fun biometricStatusRepository(impl: BiometricStatusRepositoryImpl): BiometricStatusRepository + + @Binds + @SysUISingleton fun fingerprintRepository( impl: FingerprintPropertyRepositoryImpl ): FingerprintPropertyRepository diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt new file mode 100644 index 000000000000..ad2136af4b86 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt @@ -0,0 +1,112 @@ +/* + * 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.biometrics.data.repository + +import android.hardware.biometrics.AuthenticationStateListener +import android.hardware.biometrics.BiometricManager +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR +import com.android.systemui.biometrics.shared.model.AuthenticationReason +import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.shareIn + +/** A repository for the state of biometric authentication. */ +interface BiometricStatusRepository { + /** + * The logical reason for the current fingerprint auth operation if one is on-going, otherwise + * [NotRunning]. + */ + val fingerprintAuthenticationReason: Flow<AuthenticationReason> +} + +@SysUISingleton +class BiometricStatusRepositoryImpl +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val biometricManager: BiometricManager? +) : BiometricStatusRepository { + + override val fingerprintAuthenticationReason: Flow<AuthenticationReason> = + conflatedCallbackFlow { + val updateFingerprintAuthenticateReason = { reason: AuthenticationReason -> + trySendWithFailureLogging( + reason, + TAG, + "Error sending fingerprintAuthenticateReason reason" + ) + } + + val authenticationStateListener = + object : AuthenticationStateListener.Stub() { + override fun onAuthenticationStarted(requestReason: Int) { + val authenticationReason = + when (requestReason) { + REASON_AUTH_BP -> + AuthenticationReason.BiometricPromptAuthentication + REASON_AUTH_KEYGUARD -> + AuthenticationReason.DeviceEntryAuthentication + REASON_AUTH_OTHER -> AuthenticationReason.OtherAuthentication + REASON_AUTH_SETTINGS -> + AuthenticationReason.SettingsAuthentication( + SettingsOperations.OTHER + ) + REASON_ENROLL_ENROLLING -> + AuthenticationReason.SettingsAuthentication( + SettingsOperations.ENROLL_ENROLLING + ) + REASON_ENROLL_FIND_SENSOR -> + AuthenticationReason.SettingsAuthentication( + SettingsOperations.ENROLL_FIND_SENSOR + ) + else -> AuthenticationReason.Unknown + } + updateFingerprintAuthenticateReason(authenticationReason) + } + + override fun onAuthenticationStopped() { + updateFingerprintAuthenticateReason(AuthenticationReason.NotRunning) + } + } + + updateFingerprintAuthenticateReason(AuthenticationReason.NotRunning) + biometricManager?.registerAuthenticationStateListener(authenticationStateListener) + awaitClose { + biometricManager?.unregisterAuthenticationStateListener( + authenticationStateListener + ) + } + } + .shareIn(applicationScope, started = SharingStarted.Eagerly, replay = 1) + + companion object { + private const val TAG = "BiometricStatusRepositoryImpl" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt index b9b2fd8875d9..ec3fd9f7da35 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt @@ -15,6 +15,8 @@ */ package com.android.systemui.biometrics.domain +import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor +import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl import com.android.systemui.biometrics.domain.interactor.CredentialInteractor import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor @@ -38,6 +40,12 @@ interface BiometricsDomainLayerModule { @Binds @SysUISingleton + fun providesBiometricStatusInteractor( + impl: BiometricStatusInteractorImpl + ): BiometricStatusInteractor + + @Binds + @SysUISingleton fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor @Binds diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt new file mode 100644 index 000000000000..55a2d3d7563e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt @@ -0,0 +1,74 @@ +/* + * 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.biometrics.domain.interactor + +import android.app.ActivityTaskManager +import com.android.systemui.biometrics.data.repository.BiometricStatusRepository +import com.android.systemui.biometrics.shared.model.AuthenticationReason +import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** Encapsulates business logic for interacting with biometric authentication state. */ +interface BiometricStatusInteractor { + /** + * The logical reason for the current side fingerprint sensor auth operation if one is on-going, + * filtered for when the overlay should be shown, otherwise [NotRunning]. + */ + val sfpsAuthenticationReason: Flow<AuthenticationReason> +} + +class BiometricStatusInteractorImpl +@Inject +constructor( + private val activityTaskManager: ActivityTaskManager, + biometricStatusRepository: BiometricStatusRepository, +) : BiometricStatusInteractor { + + override val sfpsAuthenticationReason: Flow<AuthenticationReason> = + biometricStatusRepository.fingerprintAuthenticationReason.map { reason: AuthenticationReason + -> + if (reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager)) { + reason + } else { + AuthenticationReason.NotRunning + } + } + + companion object { + private const val TAG = "BiometricStatusInteractor" + } +} + +/** True if the sfps overlay should always be updated for this request source, false otherwise. */ +private fun AuthenticationReason.isReasonToAlwaysUpdateSfpsOverlay( + activityTaskManager: ActivityTaskManager +): Boolean = + when (this) { + AuthenticationReason.DeviceEntryAuthentication -> false + AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER) -> + when (activityTaskManager.topClass()) { + // TODO(b/186176653): exclude fingerprint overlays from this list view + "com.android.settings.biometrics.fingerprint.FingerprintSettings" -> false + else -> true + } + else -> true + } + +internal fun ActivityTaskManager.topClass(): String = + getTasks(1).firstOrNull()?.topActivity?.className ?: "" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt index f51379940640..f4231ac01fee 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt @@ -141,7 +141,6 @@ constructor( } } } - SideFpsSensorLocation( left = sensorLeft, top = sensorTop, @@ -149,7 +148,15 @@ constructor( isSensorVerticalInDefaultOrientation = isSensorVerticalInDefaultOrientation ) } - .distinctUntilChanged() + .distinctUntilChanged( + areEquivalent = { old: SideFpsSensorLocation, new: SideFpsSensorLocation -> + old.left == new.left && + old.top == new.top && + old.length == new.length && + old.isSensorVerticalInDefaultOrientation == + new.isSensorVerticalInDefaultOrientation + } + ) .onEach { logger.sensorLocationStateChanged( it.left, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt new file mode 100644 index 000000000000..899b07e89964 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/SideFpsControllerRefactor.kt @@ -0,0 +1,53 @@ +/* + * 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.biometrics.shared + +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils +import com.android.systemui.shared.Flags + +/** Helper for reading or using the sidefps controller refactor flag state. */ +@Suppress("NOTHING_TO_INLINE") +object SideFpsControllerRefactor { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the refactor enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.sidefpsControllerRefactor() + + /** + * 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 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/biometrics/shared/model/AuthenticationReason.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/AuthenticationReason.kt new file mode 100644 index 000000000000..0c3debbe0fc4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/AuthenticationReason.kt @@ -0,0 +1,48 @@ +/* + * 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.biometrics.shared.model + +/** + * The logical reason for a fingerprint auth operation if one is on-going, otherwise [NotRunning]. + */ +sealed interface AuthenticationReason { + /** Device entry requested authentication */ + data object DeviceEntryAuthentication : AuthenticationReason + + /** Settings requested authentication */ + data class SettingsAuthentication(val settingsOperation: SettingsOperations) : + AuthenticationReason + + /** App requested authentication */ + data object BiometricPromptAuthentication : AuthenticationReason + + /** Authentication requested for other reason */ + data object OtherAuthentication : AuthenticationReason + + /** Authentication requested for unknown reason */ + data object Unknown : AuthenticationReason + + /** Authentication is not running */ + data object NotRunning : AuthenticationReason + + /** Settings operations that request biometric authentication */ + enum class SettingsOperations { + ENROLL_ENROLLING, + ENROLL_FIND_SENSOR, + OTHER + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt new file mode 100644 index 000000000000..0b3005530e6d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LottieCallback.kt @@ -0,0 +1,22 @@ +/* + * 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.biometrics.shared.model + +import com.airbnb.lottie.model.KeyPath + +/** Represents properties of a LottieAnimationView callback */ +data class LottieCallback(val keypath: KeyPath, val color: Int) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 32d9067bd9e4..90e4a3821634 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -376,6 +376,22 @@ object BiometricViewBinder { } } + // Talkback directional guidance + backgroundView.setOnHoverListener { _, event -> + launch { + viewModel.onAnnounceAccessibilityHint( + event, + accessibilityManager.isTouchExplorationEnabled + ) + } + false + } + launch { + viewModel.accessibilityHint.collect { message -> + if (message.isNotBlank()) view.announceForAccessibility(message) + } + } + // Play haptics launch { viewModel.hapticsToPlay.collect { hapticFeedbackConstant -> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt new file mode 100644 index 000000000000..a8c9446fd689 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt @@ -0,0 +1,224 @@ +/* + * 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.biometrics.ui.binder + +import android.content.Context +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import android.view.accessibility.AccessibilityEvent +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.airbnb.lottie.LottieAnimationView +import com.airbnb.lottie.LottieComposition +import com.airbnb.lottie.LottieProperty +import com.android.app.animation.Interpolators +import com.android.keyguard.KeyguardPINView +import com.android.systemui.CoreStartable +import com.android.systemui.biometrics.FpsUnlockTracker +import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor +import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning +import com.android.systemui.biometrics.shared.model.LottieCallback +import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor +import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import com.android.systemui.util.kotlin.sample +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch + +/** Binds the side fingerprint sensor indicator view to [SideFpsOverlayViewModel]. */ +@SysUISingleton +class SideFpsOverlayViewBinder +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + @Application private val applicationContext: Context, + private val biometricStatusInteractor: BiometricStatusInteractor, + private val displayStateInteractor: DisplayStateInteractor, + private val deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor, + private val fpsUnlockTracker: FpsUnlockTracker, + private val layoutInflater: LayoutInflater, + private val sideFpsProgressBarViewModel: SideFpsProgressBarViewModel, + private val sfpsSensorInteractor: SideFpsSensorInteractor, + private val windowManager: WindowManager +) : CoreStartable { + + override fun start() { + if (!SideFpsControllerRefactor.isEnabled) { + return + } + applicationScope + .launch { + combine( + biometricStatusInteractor.sfpsAuthenticationReason, + deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry, + sideFpsProgressBarViewModel.isVisible, + ::Triple + ) + .sample(displayStateInteractor.isInRearDisplayMode, ::Pair) + .collect { (combinedFlows, isInRearDisplayMode: Boolean) -> + val ( + systemServerAuthReason, + showIndicatorForDeviceEntry, + progressBarIsVisible) = + combinedFlows + if (!isInRearDisplayMode) { + if (progressBarIsVisible) { + hide() + } else if (systemServerAuthReason != NotRunning) { + show() + } else if (showIndicatorForDeviceEntry) { + show() + } else { + hide() + } + } + } + } + .invokeOnCompletion { fpsUnlockTracker.stopTracking() } + } + + private var overlayView: View? = null + private var lottie: LottieAnimationView? = null + + /** Show the side fingerprint sensor indicator */ + private fun show() { + overlayView?.let { + if (it.isAttachedToWindow) { + lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation) + lottie?.pauseAnimation() + windowManager.removeView(it) + } + } + + overlayView = layoutInflater.inflate(R.layout.sidefps_view, null, false) + val overlayViewModel = + SideFpsOverlayViewModel( + applicationContext, + biometricStatusInteractor, + deviceEntrySideFpsOverlayInteractor, + displayStateInteractor, + sfpsSensorInteractor, + sideFpsProgressBarViewModel + ) + bind(overlayView!!, overlayViewModel, fpsUnlockTracker, windowManager) + overlayView!!.visibility = View.INVISIBLE + windowManager.addView(overlayView, overlayViewModel.defaultOverlayViewParams) + } + + /** Hide the side fingerprint sensor indicator */ + private fun hide() { + if (overlayView != null) { + windowManager.removeView(overlayView) + overlayView = null + } + } + + companion object { + private const val TAG = "SideFpsOverlayViewBinder" + + /** Binds overlayView (side fingerprint sensor indicator view) to SideFpsOverlayViewModel */ + fun bind( + overlayView: View, + viewModel: SideFpsOverlayViewModel, + fpsUnlockTracker: FpsUnlockTracker, + windowManager: WindowManager + ) { + overlayView.repeatWhenAttached { + fpsUnlockTracker.startTracking() + + val lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation) + lottie.addLottieOnCompositionLoadedListener { composition: LottieComposition -> + viewModel.setLottieBounds(composition.bounds) + overlayView.visibility = View.VISIBLE + } + it.alpha = 0f + val overlayShowAnimator = + it.animate() + .alpha(1f) + .setDuration(KeyguardPINView.ANIMATION_DURATION) + .setInterpolator(Interpolators.ALPHA_IN) + + overlayShowAnimator.start() + + it.setAccessibilityDelegate( + object : View.AccessibilityDelegate() { + override fun dispatchPopulateAccessibilityEvent( + host: View, + event: AccessibilityEvent + ): Boolean { + return if ( + event.getEventType() === + android.view.accessibility.AccessibilityEvent + .TYPE_WINDOW_STATE_CHANGED + ) { + true + } else { + super.dispatchPopulateAccessibilityEvent(host, event) + } + } + } + ) + + repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + viewModel.lottieCallbacks.collect { callbacks -> + lottie.addOverlayDynamicColor(callbacks) + } + } + + launch { + viewModel.overlayViewParams.collect { params -> + windowManager.updateViewLayout(it, params) + lottie.resumeAnimation() + } + } + + launch { + viewModel.overlayViewProperties.collect { properties -> + it.rotation = properties.overlayViewRotation + lottie.setAnimation(properties.indicatorAsset) + } + } + } + } + } + } +} + +private fun LottieAnimationView.addOverlayDynamicColor(colorCallbacks: List<LottieCallback>) { + addLottieOnCompositionLoadedListener { + for (callback in colorCallbacks) { + addValueCallback(callback.keypath, LottieProperty.COLOR_FILTER) { + PorterDuffColorFilter(callback.color, PorterDuff.Mode.SRC_ATOP) + } + } + resumeAnimation() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 647aaf392ed8..6d0a58e202bd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -21,9 +21,12 @@ import android.hardware.biometrics.BiometricPrompt import android.util.Log import android.view.HapticFeedbackConstants import android.view.MotionEvent +import com.android.systemui.Flags.bpTalkback +import com.android.systemui.biometrics.UdfpsUtils import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor +import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.shared.model.BiometricModalities import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.DisplayRotation @@ -35,7 +38,9 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -49,7 +54,9 @@ class PromptViewModel constructor( displayStateInteractor: DisplayStateInteractor, promptSelectorInteractor: PromptSelectorInteractor, - @Application context: Context, + @Application private val context: Context, + private val udfpsOverlayInteractor: UdfpsOverlayInteractor, + private val udfpsUtils: UdfpsUtils ) { /** The set of modalities available for this prompt */ val modalities: Flow<BiometricModalities> = @@ -69,6 +76,11 @@ constructor( val faceIconHeight: Int = context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size) + private val _accessibilityHint = MutableSharedFlow<String>() + + /** Hint for talkback directional guidance */ + val accessibilityHint: Flow<String> = _accessibilityHint.asSharedFlow() + private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false) /** If the user is currently authenticating (i.e. at least one biometric is scanning). */ @@ -516,6 +528,40 @@ constructor( return false } + /** Sets the message used for UDFPS directional guidance */ + suspend fun onAnnounceAccessibilityHint( + event: MotionEvent, + touchExplorationEnabled: Boolean, + ): Boolean { + if (bpTalkback() && modalities.first().hasUdfps && touchExplorationEnabled) { + // TODO(b/315184924): Remove uses of UdfpsUtils + val scaledTouch = + udfpsUtils.getTouchInNativeCoordinates( + event.getPointerId(0), + event, + udfpsOverlayInteractor.udfpsOverlayParams.value + ) + if ( + !udfpsUtils.isWithinSensorArea( + event.getPointerId(0), + event, + udfpsOverlayInteractor.udfpsOverlayParams.value + ) + ) { + _accessibilityHint.emit( + udfpsUtils.onTouchOutsideOfSensorArea( + touchExplorationEnabled, + context, + scaledTouch.x, + scaledTouch.y, + udfpsOverlayInteractor.udfpsOverlayParams.value + ) + ) + } + } + return false + } + /** * Switch to the credential view. * diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt new file mode 100644 index 000000000000..ce726034913f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt @@ -0,0 +1,229 @@ +/* + * 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.biometrics.ui.viewmodel + +import android.content.Context +import android.content.res.Configuration +import android.graphics.Color +import android.graphics.PixelFormat +import android.graphics.Point +import android.graphics.Rect +import android.view.Gravity +import android.view.WindowManager +import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION +import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY +import com.airbnb.lottie.model.KeyPath +import com.android.systemui.biometrics.Utils +import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor +import com.android.systemui.biometrics.domain.model.SideFpsSensorLocation +import com.android.systemui.biometrics.shared.model.AuthenticationReason +import com.android.systemui.biometrics.shared.model.DisplayRotation +import com.android.systemui.biometrics.shared.model.LottieCallback +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor +import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel +import com.android.systemui.res.R +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged + +/** Models UI of the side fingerprint sensor indicator view. */ +class SideFpsOverlayViewModel +@Inject +constructor( + @Application private val applicationContext: Context, + biometricStatusInteractor: BiometricStatusInteractor, + deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor, + displayStateInteractor: DisplayStateInteractor, + sfpsSensorInteractor: SideFpsSensorInteractor, + sideFpsProgressBarViewModel: SideFpsProgressBarViewModel +) { + /** Contains properties of the side fingerprint sensor indicator */ + data class OverlayViewProperties( + /** The raw asset for the indicator animation */ + val indicatorAsset: Int, + /** Rotation of the overlayView */ + val overlayViewRotation: Float, + ) + + private val _lottieBounds: MutableStateFlow<Rect?> = MutableStateFlow(null) + + /** Used for setting lottie bounds once the composition has loaded. */ + fun setLottieBounds(bounds: Rect) { + _lottieBounds.value = bounds + } + + private val displayRotation = displayStateInteractor.currentRotation + private val sensorLocation = sfpsSensorInteractor.sensorLocation + + /** Default LayoutParams for the overlayView */ + val defaultOverlayViewParams: WindowManager.LayoutParams + get() = + WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS, + PixelFormat.TRANSLUCENT + ) + .apply { + title = TAG + fitInsetsTypes = 0 // overrides default, avoiding status bars during layout + gravity = Gravity.TOP or Gravity.LEFT + layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + privateFlags = PRIVATE_FLAG_TRUSTED_OVERLAY or PRIVATE_FLAG_NO_MOVE_ANIMATION + } + + private val indicatorAsset: Flow<Int> = + combine(displayRotation, sensorLocation) { rotation: DisplayRotation, sensorLocation -> + val yAligned = sensorLocation.isSensorVerticalInDefaultOrientation + val newAsset: Int = + when (rotation) { + DisplayRotation.ROTATION_0 -> + if (yAligned) { + R.raw.sfps_pulse + } else { + R.raw.sfps_pulse_landscape + } + DisplayRotation.ROTATION_180 -> + if (yAligned) { + R.raw.sfps_pulse + } else { + R.raw.sfps_pulse_landscape + } + else -> + if (yAligned) { + R.raw.sfps_pulse_landscape + } else { + R.raw.sfps_pulse + } + } + newAsset + } + .distinctUntilChanged() + + private val overlayViewRotation: Flow<Float> = + combine( + displayRotation, + sensorLocation, + ) { rotation: DisplayRotation, sensorLocation -> + val yAligned = sensorLocation.isSensorVerticalInDefaultOrientation + when (rotation) { + DisplayRotation.ROTATION_90 -> if (yAligned) 0f else 180f + DisplayRotation.ROTATION_180 -> 180f + DisplayRotation.ROTATION_270 -> if (yAligned) 180f else 0f + else -> 0f + } + } + .distinctUntilChanged() + + /** Contains properties (animation asset and view rotation) for overlayView */ + val overlayViewProperties: Flow<OverlayViewProperties> = + combine(indicatorAsset, overlayViewRotation) { asset: Int, rotation: Float -> + OverlayViewProperties(asset, rotation) + } + + /** LayoutParams for placement of overlayView (the side fingerprint sensor indicator view) */ + val overlayViewParams: Flow<WindowManager.LayoutParams> = + combine( + _lottieBounds, + sensorLocation, + displayRotation, + ) { bounds: Rect?, sensorLocation: SideFpsSensorLocation, displayRotation: DisplayRotation + -> + val topLeft = Point(sensorLocation.left, sensorLocation.top) + + if (sensorLocation.isSensorVerticalInDefaultOrientation) { + if (displayRotation == DisplayRotation.ROTATION_0) { + topLeft.x -= bounds!!.width() + } else if (displayRotation == DisplayRotation.ROTATION_270) { + topLeft.y -= bounds!!.height() + } + } else { + if (displayRotation == DisplayRotation.ROTATION_180) { + topLeft.y -= bounds!!.height() + } else if (displayRotation == DisplayRotation.ROTATION_270) { + topLeft.x -= bounds!!.width() + } + } + defaultOverlayViewParams.apply { + x = topLeft.x + y = topLeft.y + } + } + + /** List of LottieCallbacks use for adding dynamic color to the overlayView */ + val lottieCallbacks: Flow<List<LottieCallback>> = + combine( + biometricStatusInteractor.sfpsAuthenticationReason, + deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry.distinctUntilChanged(), + sideFpsProgressBarViewModel.isVisible, + ) { reason: AuthenticationReason, showIndicatorForDeviceEntry: Boolean, progressBarIsVisible + -> + val callbacks = mutableListOf<LottieCallback>() + if (showIndicatorForDeviceEntry) { + val indicatorColor = + com.android.settingslib.Utils.getColorAttrDefaultColor( + applicationContext, + com.android.internal.R.attr.materialColorPrimaryFixed + ) + val outerRimColor = + com.android.settingslib.Utils.getColorAttrDefaultColor( + applicationContext, + com.android.internal.R.attr.materialColorPrimaryFixedDim + ) + val chevronFill = + com.android.settingslib.Utils.getColorAttrDefaultColor( + applicationContext, + com.android.internal.R.attr.materialColorOnPrimaryFixed + ) + callbacks.add(LottieCallback(KeyPath(".blue600", "**"), indicatorColor)) + callbacks.add(LottieCallback(KeyPath(".blue400", "**"), outerRimColor)) + callbacks.add(LottieCallback(KeyPath(".black", "**"), chevronFill)) + } else { + if (!isDarkMode(applicationContext)) { + callbacks.add(LottieCallback(KeyPath(".black", "**"), Color.WHITE)) + } + for (key in listOf(".blue600", ".blue400")) { + callbacks.add( + LottieCallback( + KeyPath(key, "**"), + applicationContext.getColor( + com.android.settingslib.color.R.color.settingslib_color_blue400 + ), + ) + ) + } + } + callbacks + } + + companion object { + private const val TAG = "SideFpsOverlayViewModel" + } +} + +private fun isDarkMode(context: Context): Boolean { + val darkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + return darkMode == Configuration.UI_MODE_NIGHT_YES +} diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt index 1e0e16c5472f..c2a1d8fe26c3 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.bouncer.data.repository import android.os.Build import android.util.Log +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import com.android.systemui.dagger.SysUISingleton @@ -121,6 +122,7 @@ interface KeyguardBouncerRepository { fun setAlternateBouncerUIAvailable(isAvailable: Boolean) + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR fun setSideFpsShowing(isShowing: Boolean) } @@ -261,7 +263,9 @@ constructor( _isBackButtonEnabled.value = isBackButtonEnabled } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR override fun setSideFpsShowing(isShowing: Boolean) { + SideFpsControllerRefactor.assertInLegacyMode() _sideFpsShowing.value = isShowing } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt index aa7758f9380f..621ca5dc6f5a 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt @@ -28,6 +28,7 @@ import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.DejankUtils +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN @@ -116,9 +117,11 @@ constructor( /** Allow for interaction when just about fully visible */ val isInteractable: Flow<Boolean> = bouncerExpansion.map { it > 0.9 } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR val sideFpsShowing: Flow<Boolean> = repository.sideFpsShowing private var currentUserActiveUnlockRunning = false + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR /** This callback needs to be a class field so it does not get garbage collected. */ val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { @@ -135,7 +138,10 @@ constructor( } init { - keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR + if (!SideFpsControllerRefactor.isEnabled) { + keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) + } applicationScope.launch { trustRepository.isCurrentUserActiveUnlockRunning.collect { currentUserActiveUnlockRunning = it @@ -333,8 +339,10 @@ constructor( repository.setPrimaryStartDisappearAnimation(runnable) } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR /** Determine whether to show the side fps animation. */ fun updateSideFpsVisibility() { + SideFpsControllerRefactor.assertInLegacyMode() val sfpsEnabled: Boolean = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer) val fpsDetectionRunning: Boolean = keyguardUpdateMonitor.isFingerprintDetectionRunning diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt index ac3d4b6c853b..5dcd6615509d 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt @@ -27,6 +27,7 @@ import com.android.keyguard.KeyguardSecurityContainerController import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityView import com.android.keyguard.dagger.KeyguardBouncerComponent +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE import com.android.systemui.bouncer.ui.BouncerViewDelegate @@ -233,15 +234,21 @@ object KeyguardBouncerViewBinder { .collect { view.systemUiVisibility = it } } - launch { - viewModel.shouldUpdateSideFps.collect { - viewModel.updateSideFpsVisibility() + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR + if (!SideFpsControllerRefactor.isEnabled) { + launch { + viewModel.shouldUpdateSideFps.collect { + viewModel.updateSideFpsVisibility() + } } } - launch { - viewModel.sideFpsShowing.collect { - securityContainerController.updateSideFpsVisibility(it) + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR + if (!SideFpsControllerRefactor.isEnabled) { + launch { + viewModel.sideFpsShowing.collect { + securityContainerController.updateSideFpsVisibility(it) + } } } awaitCancellation() diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt index 649ae2fb1007..1c9d1f01e89e 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.bouncer.ui.viewmodel import android.view.View +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import com.android.systemui.bouncer.ui.BouncerView @@ -61,9 +62,11 @@ constructor( /** Observe whether keyguard is authenticated already. */ val keyguardAuthenticated: Flow<Boolean> = interactor.keyguardAuthenticatedBiometrics + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR /** Observe whether the side fps is showing. */ val sideFpsShowing: Flow<Boolean> = interactor.sideFpsShowing + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR /** Observe whether we should update fps is showing. */ val shouldUpdateSideFps: Flow<Unit> = merge( @@ -87,7 +90,9 @@ constructor( interactor.onMessageShown() } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR fun updateSideFpsVisibility() { + SideFpsControllerRefactor.assertInLegacyMode() interactor.updateSideFpsVisibility() } diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java index 7ac1cc796d4b..9d4ed20ca582 100644 --- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java +++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java @@ -25,11 +25,15 @@ import static com.android.systemui.controls.dagger.ControlsComponent.Visibility. import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.util.Log; import android.view.View; import android.widget.ImageView; +import androidx.annotation.Nullable; + import com.android.internal.logging.UiEventLogger; +import com.android.settingslib.Utils; import com.android.systemui.CoreStartable; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent; @@ -43,6 +47,7 @@ import com.android.systemui.dagger.qualifiers.SystemUser; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.ViewController; import com.android.systemui.util.condition.ConditionalCoreStartable; @@ -195,34 +200,70 @@ public class DreamHomeControlsComplication implements Complication { private final ActivityStarter mActivityStarter; private final Context mContext; + private final ConfigurationController mConfigurationController; private final ControlsComponent mControlsComponent; private final UiEventLogger mUiEventLogger; + private final ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { + @Override + public void onUiModeChanged() { + reloadResources(); + } + }; + @Inject DreamHomeControlsChipViewController( @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view, ActivityStarter activityStarter, Context context, + ConfigurationController configurationController, ControlsComponent controlsComponent, UiEventLogger uiEventLogger) { super(view); mActivityStarter = activityStarter; mContext = context; + mConfigurationController = configurationController; mControlsComponent = controlsComponent; mUiEventLogger = uiEventLogger; } @Override protected void onViewAttached() { - mView.setImageResource(mControlsComponent.getTileImageId()); - mView.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId())); + reloadResources(); mView.setOnClickListener(this::onClickHomeControls); + mConfigurationController.addCallback(mConfigurationListener); } @Override - protected void onViewDetached() {} + protected void onViewDetached() { + mConfigurationController.removeCallback(mConfigurationListener); + } + + private void reloadResources() { + final String title = getControlsTitle(); + if (title != null) { + mView.setContentDescription(title); + } + mView.setImageResource(mControlsComponent.getTileImageId()); + mView.setImageTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary)); + final Drawable background = mView.getBackground(); + if (background != null) { + background.setTintList( + Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface)); + } + } + + @Nullable + private String getControlsTitle() { + try { + return mContext.getString(mControlsComponent.getTileTitleId()); + } catch (Resources.NotFoundException e) { + return null; + } + } private void onClickHomeControls(View v) { if (DEBUG) Log.d(TAG, "home controls complication tapped"); diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java index 08d0595eba23..b6dcfcbfad56 100644 --- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java +++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java @@ -24,9 +24,8 @@ import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.widget.ImageView; -import com.android.settingslib.Utils; -import com.android.systemui.res.R; import com.android.systemui.complication.DreamHomeControlsComplication; +import com.android.systemui.res.R; import com.android.systemui.shared.shadow.DoubleShadowIconDrawable; import com.android.systemui.shared.shadow.DoubleShadowTextHelper; @@ -98,7 +97,7 @@ public interface DreamHomeControlsComplicationComponent { @DreamHomeControlsComplicationScope @Named(DREAM_HOME_CONTROLS_BACKGROUND_DRAWABLE) static Drawable providesHomeControlsBackground(Context context, Resources resources) { - final Drawable background = new DoubleShadowIconDrawable(createShadowInfo( + return new DoubleShadowIconDrawable(createShadowInfo( resources, R.dimen.dream_overlay_bottom_affordance_key_text_shadow_radius, R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dx, @@ -117,11 +116,6 @@ public interface DreamHomeControlsComplicationComponent { R.dimen.dream_overlay_bottom_affordance_width), resources.getDimensionPixelSize(R.dimen.dream_overlay_bottom_affordance_inset) ); - - background.setTintList( - Utils.getColorAttr(context, com.android.internal.R.attr.colorSurface)); - - return background; } private static DoubleShadowTextHelper.ShadowInfo createShadowInfo(Resources resources, diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt index ae3b501d3006..e96660fe4f79 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt @@ -25,7 +25,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.Compile import com.android.systemui.util.asIndenting -import com.android.systemui.util.withIncreasedIndent +import com.android.systemui.util.printCollection import dagger.Binds import dagger.Module import dagger.multibindings.ClassKey @@ -59,10 +59,8 @@ abstract class FlagDependenciesBase( override fun dump(pw: PrintWriter, args: Array<out String>) { pw.asIndenting().run { - println("allDependencies: ${allDependencies.size}") - withIncreasedIndent { allDependencies.forEach(::println) } - println("unmetDependencies: ${unmetDependencies.size}") - withIncreasedIndent { unmetDependencies.forEach(::println) } + printCollection("allDependencies", allDependencies) + printCollection("unmetDependencies", unmetDependencies) } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt index ae67e60b75f7..4d89a826b60a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlagUtils.kt @@ -79,6 +79,22 @@ object RefactorFlagUtils { check(!isEnabled) { "Legacy code path not supported when $flagName is enabled." } /** + * Called to ensure the new code is only run when the flag is enabled. This will throw an + * exception if the flag is disabled to ensure that the refactor author catches issues in + * testing. + * + * Example usage: + * ``` + * public void setSomeNewController(SomeController someController) { + * SomeRefactor.assertInNewMode(); + * mSomeController = someController; + * } + * ```` + */ + inline fun assertInNewMode(isEnabled: Boolean, flagName: Any) = + check(isEnabled) { "New code path not supported when $flagName is disabled." } + + /** * This will [Log.wtf] with the given message, assuming [ASSERT_TAG] is loggable at that level. * This means an engineer can prevent this from crashing by running the command: * ``` diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 33dd3d9bea16..edf964815ca1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -35,7 +35,7 @@ import static android.view.WindowManager.TransitionFlags; import static android.view.WindowManager.TransitionOldType; import static android.view.WindowManager.TransitionType; -import static com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER; +import static com.android.systemui.Flags.refactorGetCurrentUser; import android.annotation.NonNull; import android.app.ActivityManager; @@ -609,7 +609,7 @@ public class KeyguardService extends Service { public void setCurrentUser(int userId) { trace("Deprecated/NOT USED: setCurrentUser userId=" + userId); checkPermission(); - if (!mFlags.isEnabled(REFACTOR_GETCURRENTUSER)) { + if (!refactorGetCurrentUser()) { mKeyguardViewMediator.setCurrentUser(userId); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 57f3b7071d1d..d7a1906f68e4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -38,7 +38,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE; import static com.android.systemui.DejankUtils.whitelistIpcs; -import static com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER; +import static com.android.systemui.Flags.refactorGetCurrentUser; import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -617,7 +617,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void onUserSwitching(int userId) { if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId)); synchronized (KeyguardViewMediator.this) { - if (mFeatureFlags.isEnabled(REFACTOR_GETCURRENTUSER)) { + if (refactorGetCurrentUser()) { notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId)); } resetKeyguardDonePendingLocked(); @@ -1502,7 +1502,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - if (!mFeatureFlags.isEnabled(REFACTOR_GETCURRENTUSER)) { + if (!refactorGetCurrentUser()) { KeyguardUpdateMonitor.setCurrentUser(mUserTracker.getUserId()); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt index 96386f3dc596..9a13558d3327 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt @@ -41,6 +41,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn /** Encapsulates state about device entry fingerprint auth mechanism. */ @@ -61,6 +62,9 @@ interface DeviceEntryFingerprintAuthRepository { /** Provide the current status of fingerprint authentication. */ val authenticationStatus: Flow<FingerprintAuthenticationStatus> + + /** Indicates whether to update the side fingerprint sensor indicator visibility. */ + val shouldUpdateIndicatorVisibility: Flow<Boolean> } /** @@ -256,6 +260,37 @@ constructor( awaitClose { keyguardUpdateMonitor.removeCallback(callback) } } + override val shouldUpdateIndicatorVisibility: Flow<Boolean> = + conflatedCallbackFlow { + val sendShouldUpdateIndicatorVisibility = + { shouldUpdateIndicatorVisibility: Boolean -> + trySendWithFailureLogging( + shouldUpdateIndicatorVisibility, + TAG, + "Error sending shouldUpdateIndicatorVisibility " + + "$shouldUpdateIndicatorVisibility" + ) + } + + val callback = + object : KeyguardUpdateMonitorCallback() { + override fun onBiometricRunningStateChanged( + running: Boolean, + biometricSourceType: BiometricSourceType? + ) { + sendShouldUpdateIndicatorVisibility(true) + } + override fun onStrongAuthStateChanged(userId: Int) { + sendShouldUpdateIndicatorVisibility(true) + } + } + sendShouldUpdateIndicatorVisibility(false) + keyguardUpdateMonitor.registerCallback(callback) + awaitClose { keyguardUpdateMonitor.removeCallback(callback) } + } + .flowOn(mainDispatcher) + .shareIn(scope, started = SharingStarted.WhileSubscribed(), replay = 1) + companion object { const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt new file mode 100644 index 000000000000..de15fd6a958f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.content.Context +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository +import com.android.systemui.res.R +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +/** + * Encapsulates business logic for device entry events that impact the side fingerprint sensor + * overlay. + */ +@SysUISingleton +class DeviceEntrySideFpsOverlayInteractor +@Inject +constructor( + @Application private val context: Context, + deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, + private val primaryBouncerInteractor: PrimaryBouncerInteractor, + alternateBouncerInteractor: AlternateBouncerInteractor, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, +) { + + init { + alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG) + } + + private val showIndicatorForPrimaryBouncer: Flow<Boolean> = + merge( + primaryBouncerInteractor.isShowing, + primaryBouncerInteractor.startingToHide, + primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(), + deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it } + ) + .map { shouldShowIndicatorForPrimaryBouncer() } + + private val showIndicatorForAlternateBouncer: Flow<Boolean> = + alternateBouncerInteractor.isVisible + + /** + * Indicates whether the primary or alternate bouncers request showing the side fingerprint + * sensor indicator. + */ + val showIndicatorForDeviceEntry: Flow<Boolean> = + combine(showIndicatorForPrimaryBouncer, showIndicatorForAlternateBouncer) { + showForPrimaryBouncer, + showForAlternateBouncer -> + showForPrimaryBouncer || showForAlternateBouncer + } + + private fun shouldShowIndicatorForPrimaryBouncer(): Boolean { + val sfpsEnabled: Boolean = + context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer) + val sfpsDetectionRunning = keyguardUpdateMonitor.isFingerprintDetectionRunning + val isUnlockingWithFpAllowed = keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed + return primaryBouncerInteractor.isBouncerShowing() && + sfpsEnabled && + sfpsDetectionRunning && + isUnlockingWithFpAllowed && + !primaryBouncerInteractor.isAnimatingAway() + } + + companion object { + private const val TAG = "DeviceEntrySideFpsOverlayInteractor" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt index 560e4adf956a..9a6dca3c4958 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt @@ -21,6 +21,7 @@ import android.graphics.Point import com.android.systemui.CoreStartable import com.android.systemui.Flags import com.android.systemui.biometrics.SideFpsController +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.ui.view.SideFpsProgressBar @@ -46,6 +47,7 @@ constructor( private val viewModel: SideFpsProgressBarViewModel, private val view: SideFpsProgressBar, @Application private val applicationScope: CoroutineScope, + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR private val sfpsController: dagger.Lazy<SideFpsController>, private val logger: SideFpsLogger, private val commandRegistry: CommandRegistry, @@ -109,12 +111,14 @@ constructor( view.updateView(visible, location, length, thickness, rotation) // We have to hide the SFPS indicator as the progress bar will // be shown at the same location - if (visible) { - logger.hidingSfpsIndicator() - sfpsController.get().hideIndicator() - } else if (fpDetectRunning) { - logger.showingSfpsIndicator() - sfpsController.get().showIndicator() + if (!SideFpsControllerRefactor.isEnabled) { + if (visible) { + logger.hidingSfpsIndicator() + sfpsController.get().hideIndicator() + } else if (fpDetectRunning) { + logger.showingSfpsIndicator() + sfpsController.get().showIndicator() + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt index 2d0712c7f9cc..1dbf1f14b569 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.animation.ValueAnimator import android.content.Context import android.graphics.Point +import androidx.annotation.VisibleForTesting import androidx.core.animation.doOnEnd import com.android.systemui.Flags import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor @@ -213,4 +214,9 @@ constructor( } } } + + @VisibleForTesting + fun setVisible(isVisible: Boolean) { + _visible.value = isVisible + } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 9846f4b6c980..e660b97b5c6b 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -69,7 +69,6 @@ import androidx.annotation.DimenRes; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.policy.GestureNavigationSettingsObserver; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; @@ -81,6 +80,7 @@ import com.android.systemui.plugins.NavigationEdgeBackPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputChannelCompat; @@ -1113,6 +1113,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // Capture inputs mInputMonitor.pilferPointers(); if (mBackAnimation != null) { + mBackAnimation.onPilferPointers(); // Notify FalsingManager that an intentional gesture has occurred. mFalsingManager.isFalseTouch(BACK_GESTURE); } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index 270bfbe4274d..0e7e69b97d8a 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -263,7 +263,11 @@ constructor( * If the shortcut entry `android:enabled` is set to `true`, the shortcut will be visible in the * Widget Picker to all users. */ + // TODO(b/316332684) + @Suppress("UNREACHABLE_CODE") fun setNoteTaskShortcutEnabled(value: Boolean, user: UserHandle) { + return // shortcut should not be enabled until additional features are implemented. + if (!userManager.isUserUnlocked(user)) { debugLog { "setNoteTaskShortcutEnabled call but user locked: user=$user" } return diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt index 6d55d0549d2e..de490a58a498 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt @@ -27,7 +27,6 @@ import android.os.UserManager import android.util.Log import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.res.R import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon @@ -43,6 +42,7 @@ import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEnabledKey import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskInfoResolver +import com.android.systemui.res.R import com.android.systemui.stylus.StylusManager import dagger.Lazy import java.util.concurrent.Executor @@ -100,7 +100,8 @@ constructor( isEnabled && isUserUnlocked && isDefaultNotesAppSet && - (isConfigSelected || isStylusEverUsed) + isConfigSelected && + isStylusEverUsed ) { val contentDescription = ContentDescription.Resource(pickerNameResourceId) val icon = Icon.Resource(pickerIconResourceId, contentDescription) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 6c930b1f3d17..3e50dd35749f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -43,6 +43,9 @@ import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.util.ViewController; import com.android.systemui.util.animation.DisappearParameters; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; @@ -50,9 +53,6 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.stream.Collectors; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; - /** * Controller for QSPanel views. * @@ -232,7 +232,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr mQsTileRevealController.updateRevealedTiles(tiles); } boolean shouldChange = false; - if (tiles.size() == mRecords.size()) { + if (tiles.size() <= mRecords.size()) { int i = 0; for (QSTile tile : tiles) { if (tile != mRecords.get(i).tile) { @@ -241,6 +241,17 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr } i++; } + + // If the first tiles are the same as the new ones, remove any extras. + if (!shouldChange) { + while (i < mRecords.size()) { + QSPanelControllerBase.TileRecord record = mRecords.get(i); + mView.removeTile(record); + record.tile.removeCallback(record.callback); + i++; + } + mCachedSpecs = getTilesSpecs(); + } } else { shouldChange = true; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt index 935d07229ae5..42bee3c8f877 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt @@ -22,8 +22,8 @@ class QSPipelineFlagsRepository @Inject constructor() { AconfigFlags.FLAG_QS_NEW_PIPELINE ) - fun assertNewTilesInLegacyMode() = - RefactorFlagUtils.assertInLegacyMode( + fun assertNewTiles() = + RefactorFlagUtils.assertInNewMode( AconfigFlags.qsNewTiles(), AconfigFlags.FLAG_QS_NEW_TILES ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 38bbce45e143..17e6375967fc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -19,6 +19,7 @@ import android.util.Log; import androidx.annotation.Nullable; +import com.android.systemui.accessibility.qs.QSAccessibilityModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; @@ -41,7 +42,7 @@ import javax.inject.Provider; * com.android.systemui.qs.tiles.DreamTile#TILE_SPEC}) * * After, create or find an existing Module class to house the tile's binding method (e.g. {@link - * com.android.systemui.accessibility.AccessibilityModule}). If creating a new module, add your + * QSAccessibilityModule}). If creating a new module, add your * module to the SystemUI dagger graph by including it in an appropriate module. */ @SysUISingleton 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 0434b2d89e32..66da8bdd0311 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -42,6 +42,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.res.R +import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController @@ -63,6 +64,7 @@ constructor( private val keyguardStateController: KeyguardStateController, private val dialogLaunchAnimator: DialogLaunchAnimator, private val sysuiDialogFactory: SystemUIDialog.Factory, + private val userContextProvider: UserContextProvider, ) : QSTileImpl<QSTile.BooleanState>( host, @@ -100,7 +102,7 @@ constructor( private fun showPrompt(view: View?) { val dialog: AlertDialog = - RecordIssueDialogDelegate(sysuiDialogFactory) { + RecordIssueDialogDelegate(sysuiDialogFactory, userContextProvider) { isRecording = true refreshState() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt new file mode 100644 index 000000000000..d0437a7210f1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt @@ -0,0 +1,31 @@ +/* + * 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.qs.tiles.base.viewmodel + +import com.android.systemui.dagger.qualifiers.Application +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob + +/** Creates a [CoroutineScope] for the [QSTileViewModelImpl]. */ +class QSTileCoroutineScopeFactory +@Inject +constructor(@Application private val applicationScope: CoroutineScope) { + + fun create(): CoroutineScope = + CoroutineScope(applicationScope.coroutineContext + SupervisorJob()) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt index ae554d954580..ffa3b543736b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt @@ -82,6 +82,7 @@ sealed interface QSTileViewModelFactory<T> { qsTileLogger, systemClock, backgroundDispatcher, + component.coroutineScope(), ) } } @@ -101,6 +102,7 @@ sealed interface QSTileViewModelFactory<T> { private val qsTileConfigProvider: QSTileConfigProvider, private val systemClock: SystemClock, @Background private val backgroundDispatcher: CoroutineDispatcher, + private val coroutineScopeFactory: QSTileCoroutineScopeFactory, ) : QSTileViewModelFactory<T> { /** @@ -130,6 +132,7 @@ sealed interface QSTileViewModelFactory<T> { qsTileLogger, systemClock, backgroundDispatcher, + coroutineScopeFactory.create(), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt index 9fe316f99136..45c6fffe6bd1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt @@ -39,7 +39,6 @@ import java.io.PrintWriter import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -82,7 +81,7 @@ class QSTileViewModelImpl<DATA_TYPE>( private val qsTileLogger: QSTileLogger, private val systemClock: SystemClock, private val backgroundDispatcher: CoroutineDispatcher, - private val tileScope: CoroutineScope = CoroutineScope(SupervisorJob()), + private val tileScope: CoroutineScope, ) : QSTileViewModel, Dumpable { private val users: MutableStateFlow<UserHandle> = diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt index 52e49f9d2653..382cfe267aae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt @@ -45,7 +45,7 @@ constructor( ) : QSFactory { init { - QSPipelineFlagsRepository.assertNewTilesInLegacyMode() + QSPipelineFlagsRepository.assertNewTiles() for (viewModelTileSpec in tileMap.keys) { require(qsTileConfigProvider.hasConfig(viewModelTileSpec)) { "No config for $viewModelTileSpec" @@ -56,7 +56,7 @@ constructor( override fun createTile(tileSpec: String): QSTile? { val viewModel: QSTileViewModel = when (val spec = TileSpec.create(tileSpec)) { - is TileSpec.CustomTileSpec -> createCustomTileViewModel(spec) + is TileSpec.CustomTileSpec -> null is TileSpec.PlatformTileSpec -> tileMap[tileSpec]?.get() is TileSpec.Invalid -> null } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt new file mode 100644 index 000000000000..1efbfd70fa98 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt @@ -0,0 +1,51 @@ +/* + * 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.qs.tiles.impl.colorcorrection.domain + +import android.content.res.Resources +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [ColorCorrectionTileModel] to [QSTileState]. */ +class ColorCorrectionTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Resources.Theme, +) : QSTileDataToStateMapper<ColorCorrectionTileModel> { + + override fun map(config: QSTileConfig, data: ColorCorrectionTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + val subtitleArray = resources.getStringArray(R.array.tile_states_color_correction) + + if (data.isEnabled) { + activationState = QSTileState.ActivationState.ACTIVE + secondaryLabel = subtitleArray[2] + } else { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = subtitleArray[1] + } + contentDescription = label + supportedActions = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt new file mode 100644 index 000000000000..cd33d451ba81 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt @@ -0,0 +1,43 @@ +/* + * 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.qs.tiles.impl.colorcorrection.domain.interactor + +import android.os.UserHandle +import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map + +/** Observes color correction state changes providing the [ColorCorrectionTileModel]. */ +class ColorCorrectionTileDataInteractor +@Inject +constructor( + private val colorCorrectionRepository: ColorCorrectionRepository, +) : QSTileDataInteractor<ColorCorrectionTileModel> { + + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<ColorCorrectionTileModel> { + return colorCorrectionRepository.isEnabled(user).map { ColorCorrectionTileModel(it) } + } + override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt new file mode 100644 index 000000000000..d1838029db4e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt @@ -0,0 +1,54 @@ +/* + * 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.qs.tiles.impl.colorcorrection.domain.interactor + +import android.content.Intent +import android.provider.Settings +import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject + +/** Handles color correction tile clicks. */ +class ColorCorrectionUserActionInteractor +@Inject +constructor( + private val colorCorrectionRepository: ColorCorrectionRepository, + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, +) : QSTileUserActionInteractor<ColorCorrectionTileModel> { + + override suspend fun handleInput(input: QSTileInput<ColorCorrectionTileModel>): Unit = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + colorCorrectionRepository.setIsEnabled( + !data.isEnabled, + user, + ) + } + is QSTileUserAction.LongClick -> { + qsTileIntentUserActionHandler.handle( + action.view, + Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + ) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt new file mode 100644 index 000000000000..66487e1bba60 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt @@ -0,0 +1,24 @@ +/* + * 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.qs.tiles.impl.colorcorrection.domain.model + +/** + * Color correction tile model. + * + * @param isEnabled is true when the color correction is enabled; + */ +@JvmInline value class ColorCorrectionTileModel(val isEnabled: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt index 6d7d88fbfa79..92b0f3aa604d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt @@ -16,46 +16,103 @@ package com.android.systemui.qs.tiles.impl.custom.data.repository +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.os.UserHandle +import androidx.annotation.GuardedBy import com.android.systemui.common.coroutine.ConflatedCallbackFlow -import com.android.systemui.qs.external.TileServiceManager +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.pipeline.shared.TileSpec -import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundScope -import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileUser +import com.android.systemui.qs.tiles.impl.di.QSTileScope import javax.inject.Inject +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.launch interface CustomTilePackageUpdatesRepository { - val packageChanges: Flow<Unit> + fun getPackageChangesForUser(user: UserHandle): Flow<Unit> } -@CustomTileBoundScope +@QSTileScope class CustomTilePackageUpdatesRepositoryImpl @Inject constructor( - tileSpec: TileSpec.CustomTileSpec, - @CustomTileUser user: UserHandle, - serviceManager: TileServiceManager, - defaultsRepository: CustomTileDefaultsRepository, - @CustomTileBoundScope boundScope: CoroutineScope, + private val tileSpec: TileSpec.CustomTileSpec, + @Application private val context: Context, + @QSTileScope private val tileScope: CoroutineScope, + @Background private val backgroundCoroutineContext: CoroutineContext, ) : CustomTilePackageUpdatesRepository { - override val packageChanges: Flow<Unit> = + @GuardedBy("perUserCache") + private val perUserCache: MutableMap<UserHandle, Flow<Unit>> = mutableMapOf() + + override fun getPackageChangesForUser(user: UserHandle): Flow<Unit> = + synchronized(perUserCache) { + perUserCache.getOrPut(user) { + createPackageChangesFlowForUser(user) + .onCompletion { + // clear cache when nobody listens + synchronized(perUserCache) { perUserCache.remove(user) } + } + .flowOn(backgroundCoroutineContext) + .shareIn(tileScope, SharingStarted.WhileSubscribed()) + } + } + + @SuppressLint( + "MissingPermission", // android.permission.INTERACT_ACROSS_USERS_FULL + "UnspecifiedRegisterReceiverFlag", + "RegisterReceiverViaContext", + ) + private fun createPackageChangesFlowForUser(user: UserHandle): Flow<Unit> = ConflatedCallbackFlow.conflatedCallbackFlow { - serviceManager.setTileChangeListener { changedComponentName -> - if (changedComponentName == tileSpec.componentName) { - trySend(Unit) + val receiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + launch { send(intent) } + } } + context.registerReceiverAsUser( + receiver, + user, + INTENT_FILTER, + /* broadcastPermission = */ null, + /* scheduler = */ null, + ) + + awaitClose { context.unregisterReceiver(receiver) } + } + .filter { intent -> + intent ?: return@filter false + if (intent.action?.let(INTENT_FILTER::matchAction) != true) { + return@filter false } + val changedComponentNames = + intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST) + changedComponentNames?.contains(tileSpec.componentName.packageName) == true + } + .map {} + + private companion object { + + val INTENT_FILTER = + IntentFilter().apply { + addDataScheme(IntentFilter.SCHEME_PACKAGE) - awaitClose { serviceManager.setTileChangeListener(null) } + addAction(Intent.ACTION_PACKAGE_CHANGED) } - .onEach { defaultsRepository.requestNewDefaults(user, tileSpec.componentName, true) } - .shareIn(boundScope, SharingStarted.WhileSubscribed()) + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt index d956fdebcd32..ba8b23ab795a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles.impl.custom.di import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.base.viewmodel.QSTileCoroutineScopeFactory import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor @@ -26,13 +27,15 @@ import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefau import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl -import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel +import com.android.systemui.qs.tiles.impl.di.QSTileScope import dagger.Binds import dagger.Module +import dagger.Provides +import kotlinx.coroutines.CoroutineScope /** Provides bindings for QSTile interfaces */ -@Module(subcomponents = [CustomTileBoundComponent::class]) +@Module interface CustomTileModule { @Binds @@ -54,4 +57,13 @@ interface CustomTileModule { ): CustomTileDefaultsRepository @Binds fun bindCustomTileRepository(impl: CustomTileRepositoryImpl): CustomTileRepository + + companion object { + + @Provides + @QSTileScope + fun provideCustomTileCoroutineScope( + scopeFactory: QSTileCoroutineScopeFactory + ): CoroutineScope = scopeFactory.create() + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt index b3d916a86144..0ed46e73958d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles.impl.di import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import kotlinx.coroutines.CoroutineScope /** * Base QS tile component. It should be used with [QSTileScope] to create a custom tile scoped @@ -32,4 +33,12 @@ interface QSTileComponent<T> { fun userActionInteractor(): QSTileUserActionInteractor<T> fun dataToStateMapper(): QSTileDataToStateMapper<T> + + /** + * Use [com.android.systemui.qs.tiles.base.viewmodel.QSTileCoroutineScopeFactory] to create a + * [CoroutineScope] provided by this method. This enables you to use the same scope the + * [com.android.systemui.qs.tiles.viewmodel.QSTileViewModel] uses. This scope is cancelled when + * the view model is destroyed. + */ + @QSTileScope fun coroutineScope(): CoroutineScope } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt index 8221c635741b..5bf44fa5b143 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt @@ -17,6 +17,9 @@ package com.android.systemui.recordissue import android.annotation.SuppressLint +import android.app.Activity +import android.app.BroadcastOptions +import android.app.PendingIntent import android.content.Context import android.content.res.ColorStateList import android.graphics.Color @@ -28,10 +31,14 @@ import android.widget.Button import android.widget.PopupMenu import android.widget.Switch import com.android.systemui.res.R +import com.android.systemui.screenrecord.RecordingService +import com.android.systemui.screenrecord.ScreenRecordingAudioSource +import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.SystemUIDialog class RecordIssueDialogDelegate( private val factory: SystemUIDialog.Factory, + private val userContextProvider: UserContextProvider, private val onStarted: Runnable ) : SystemUIDialog.Delegate { @@ -46,6 +53,9 @@ class RecordIssueDialogDelegate( setNegativeButton(R.string.cancel) { _, _ -> dismiss() } setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() + if (screenRecordSwitch.isChecked) { + requestScreenCapture() + } dismiss() } } @@ -85,4 +95,19 @@ class RecordIssueDialogDelegate( show() } } + + private fun requestScreenCapture() = + PendingIntent.getForegroundService( + userContextProvider.userContext, + RecordingService.REQUEST_CODE, + RecordingService.getStartIntent( + userContextProvider.userContext, + Activity.RESULT_OK, + ScreenRecordingAudioSource.NONE.ordinal, + false, + null + ), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle()) } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index d42fde617394..47518bbee57a 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -44,6 +44,11 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICA import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING +import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled +import com.android.systemui.util.asIndenting +import com.android.systemui.util.printSection +import com.android.systemui.util.println +import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -94,6 +99,15 @@ constructor( } } + override fun dump(pw: PrintWriter, args: Array<out String>) = + pw.asIndenting().run { + printSection("SceneContainerFlags") { + println("isEnabled", flags.isEnabled()) + printSection("requirementDescription") { println(flags.requirementDescription()) } + println("flexiNotifsEnabled", flags.flexiNotifsEnabled()) + } + } + /** Updates the visibility of the scene container. */ private fun hydrateVisibility() { applicationScope.launch { diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 886fa70d715d..2b9ad50c1257 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -18,8 +18,8 @@ package com.android.systemui.theme; import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.Flags.themeOverlayControllerWakefulnessDeprecation; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET; @@ -364,15 +364,23 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction()); - boolean isManagedProfile = mUserManager.isManagedProfile( - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); - if (newWorkProfile) { - if (!mDeviceProvisionedController.isCurrentUserSetup() && isManagedProfile) { + boolean newProfile = Intent.ACTION_PROFILE_ADDED.equals(intent.getAction()); + if (newProfile) { + UserHandle newUserHandle = intent.getParcelableExtra(Intent.EXTRA_USER, + android.os.UserHandle.class); + boolean isManagedProfile = + mUserManager.isManagedProfile(newUserHandle.getIdentifier()); + if (!mDeviceProvisionedController.isUserSetup(newUserHandle.getIdentifier()) + && isManagedProfile) { Log.i(TAG, "User setup not finished when " + intent.getAction() + " was received. Deferring... Managed profile? " + isManagedProfile); return; } + if (android.os.Flags.allowPrivateProfile() && isPrivateProfile(newUserHandle)) { + mDeferredThemeEvaluation = true; + Log.i(TAG, "Deferring theme for private profile till user setup is complete"); + return; + } if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); reevaluateSystemTheme(true /* forceReload */); } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) { @@ -432,7 +440,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { public void start() { if (DEBUG) Log.d(TAG, "Start"); final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(Intent.ACTION_PROFILE_ADDED); filter.addAction(Intent.ACTION_WALLPAPER_CHANGED); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor, UserHandle.ALL); @@ -608,6 +616,15 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { return new FabricatedOverlay.Builder("com.android.systemui", name, "android").build(); } + @VisibleForTesting + protected boolean isPrivateProfile(UserHandle userHandle) { + Context usercontext = mContext.createContextAsUser(userHandle,0); + if (usercontext.getSystemService(UserManager.class).isPrivateProfile()) { + return true; + } + return false; + } + private void createOverlays(int color) { boolean nightMode = isNightMode(); mColorScheme = new ColorScheme(color, nightMode, mThemeStyle); @@ -784,7 +801,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { Set<UserHandle> managedProfiles = new HashSet<>(); for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) { - if (userInfo.isManagedProfile()) { + if (userInfo.isProfile()) { managedProfiles.add(userInfo.getUserHandle()); } } diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt index 3ed05aac3e10..0fb4b43865aa 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt @@ -2,9 +2,8 @@ package com.android.systemui.user.domain.interactor import android.annotation.UserIdInt import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.Flags.refactorGetCurrentUser import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER import com.android.systemui.user.data.repository.UserRepository import javax.inject.Inject import kotlinx.coroutines.flow.distinctUntilChanged @@ -12,12 +11,7 @@ import kotlinx.coroutines.flow.map /** Encapsulates business logic to interact the selected user */ @SysUISingleton -class SelectedUserInteractor -@Inject -constructor( - private val repository: UserRepository, - private val flags: FeatureFlagsClassic, -) { +class SelectedUserInteractor @Inject constructor(private val repository: UserRepository) { /** Flow providing the ID of the currently selected user. */ val selectedUser = repository.selectedUserInfo.map { it.id }.distinctUntilChanged() @@ -34,7 +28,7 @@ constructor( @UserIdInt @JvmOverloads fun getSelectedUserId(bypassFlag: Boolean = false): Int { - return if (bypassFlag || flags.isEnabled(REFACTOR_GETCURRENTUSER)) { + return if (bypassFlag || refactorGetCurrentUser()) { repository.getSelectedUserInfo().id } else { KeyguardUpdateMonitor.getCurrentUser() diff --git a/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt b/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt index 5b0943a77979..a89fdda01537 100644 --- a/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt @@ -60,6 +60,12 @@ fun IndentingPrintWriter.withIncreasedIndent(runnable: Runnable) { fun IndentingPrintWriter.println(label: String, value: Any?) = append(label).append('=').println(value) +/** Print a section with a header using the given name and an indented body */ +inline fun IndentingPrintWriter.printSection(sectionName: String, block: () -> Unit) { + append(sectionName).println(":") + withIncreasedIndent(block) +} + @JvmOverloads inline fun <T> IndentingPrintWriter.printCollection( label: String, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index ea20d29556dc..91219f02818c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -47,12 +47,14 @@ import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorI import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl +import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.events.ANIMATING_OUT +import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -101,6 +103,12 @@ open class AuthContainerViewTest : SysuiTestCase() { lateinit var interactionJankMonitor: InteractionJankMonitor @Mock lateinit var vibrator: VibratorHelper + @Mock + lateinit var udfpsUtils: UdfpsUtils + @Mock + lateinit var authController: AuthController + @Mock + lateinit var selectedUserInteractor: SelectedUserInteractor private val testScope = TestScope(StandardTestDispatcher()) private val fakeExecutor = FakeExecutor(FakeSystemClock()) @@ -123,6 +131,7 @@ open class AuthContainerViewTest : SysuiTestCase() { private lateinit var displayRepository: FakeDisplayRepository private lateinit var displayStateInteractor: DisplayStateInteractor + private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor) @@ -140,6 +149,12 @@ open class AuthContainerViewTest : SysuiTestCase() { displayStateRepository, displayRepository, ) + udfpsOverlayInteractor = + UdfpsOverlayInteractor( + authController, + selectedUserInteractor, + testScope.backgroundScope, + ) } @After @@ -532,6 +547,8 @@ open class AuthContainerViewTest : SysuiTestCase() { displayStateInteractor, promptSelectorInteractor, context, + udfpsOverlayInteractor, + udfpsUtils ), { credentialViewModel }, Handler(TestableLooper.get(this).looper), diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index 8e54eb7334dd..4c510ee0b8a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -27,6 +27,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.keyguard.logging.KeyguardLogger +import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION import com.android.systemui.SysuiTestCase import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FeatureFlags @@ -40,6 +41,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.leak.RotationUtils import com.android.systemui.util.mockito.any +import javax.inject.Provider import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -51,14 +53,14 @@ import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.MockitoSession import org.mockito.quality.Strictness -import javax.inject.Provider + @SmallTest @RunWith(AndroidTestingRunner::class) @@ -246,6 +248,7 @@ class AuthRippleControllerTest : SysuiTestCase() { @Test @RunWithLooper(setAsMainLooper = true) fun testAnimatorRunWhenWakeAndUnlock_fingerprint() { + mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION) val fpsLocation = Point(5, 5) `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) controller.onViewAttached() @@ -266,6 +269,7 @@ class AuthRippleControllerTest : SysuiTestCase() { @Test @RunWithLooper(setAsMainLooper = true) fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() { + mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION) val faceLocation = Point(5, 5) `when`(authController.faceSensorLocation).thenReturn(faceLocation) controller.onViewAttached() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt new file mode 100644 index 000000000000..27d93eb31922 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt @@ -0,0 +1,176 @@ +/* + * 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.biometrics.data.repository + +import android.hardware.biometrics.AuthenticationStateListener +import android.hardware.biometrics.BiometricManager +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER +import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING +import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR +import android.platform.test.annotations.RequiresFlagsEnabled +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.shared.model.AuthenticationReason +import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR) +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class BiometricStatusRepositoryTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var biometricManager: BiometricManager + + private lateinit var underTest: BiometricStatusRepository + + private val testScope = TestScope(StandardTestDispatcher()) + + @Before + fun setUp() { + underTest = BiometricStatusRepositoryImpl(testScope.backgroundScope, biometricManager) + } + + @Test + fun updatesFingerprintAuthenticationReason_whenBiometricPromptAuthenticationStarted() = + testScope.runTest { + val fingerprintAuthenticationReason by + collectLastValue(underTest.fingerprintAuthenticationReason) + runCurrent() + + val listener = biometricManager.captureListener() + + assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + listener.onAuthenticationStarted(REASON_AUTH_BP) + assertThat(fingerprintAuthenticationReason) + .isEqualTo(AuthenticationReason.BiometricPromptAuthentication) + } + + @Test + fun updatesFingerprintAuthenticationReason_whenDeviceEntryAuthenticationStarted() = + testScope.runTest { + val fingerprintAuthenticationReason by + collectLastValue(underTest.fingerprintAuthenticationReason) + runCurrent() + + val listener = biometricManager.captureListener() + + assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + listener.onAuthenticationStarted(REASON_AUTH_KEYGUARD) + assertThat(fingerprintAuthenticationReason) + .isEqualTo(AuthenticationReason.DeviceEntryAuthentication) + } + + @Test + fun updatesFingerprintAuthenticationReason_whenOtherAuthenticationStarted() = + testScope.runTest { + val fingerprintAuthenticationReason by + collectLastValue(underTest.fingerprintAuthenticationReason) + runCurrent() + + val listener = biometricManager.captureListener() + + assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + listener.onAuthenticationStarted(REASON_AUTH_OTHER) + assertThat(fingerprintAuthenticationReason) + .isEqualTo(AuthenticationReason.OtherAuthentication) + } + + @Test + fun updatesFingerprintAuthenticationReason_whenSettingsAuthenticationStarted() = + testScope.runTest { + val fingerprintAuthenticationReason by + collectLastValue(underTest.fingerprintAuthenticationReason) + runCurrent() + + val listener = biometricManager.captureListener() + + assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + listener.onAuthenticationStarted(REASON_AUTH_SETTINGS) + assertThat(fingerprintAuthenticationReason) + .isEqualTo(AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER)) + } + + @Test + fun updatesFingerprintAuthenticationReason_whenEnrollmentAuthenticationStarted() = + testScope.runTest { + val fingerprintAuthenticationReason by + collectLastValue(underTest.fingerprintAuthenticationReason) + runCurrent() + + val listener = biometricManager.captureListener() + + assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + listener.onAuthenticationStarted(REASON_ENROLL_FIND_SENSOR) + assertThat(fingerprintAuthenticationReason) + .isEqualTo( + AuthenticationReason.SettingsAuthentication( + SettingsOperations.ENROLL_FIND_SENSOR + ) + ) + + listener.onAuthenticationStarted(REASON_ENROLL_ENROLLING) + assertThat(fingerprintAuthenticationReason) + .isEqualTo( + AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING) + ) + } + + @Test + fun updatesFingerprintAuthenticationReason_whenAuthenticationStopped() = + testScope.runTest { + val fingerprintAuthenticationReason by + collectLastValue(underTest.fingerprintAuthenticationReason) + runCurrent() + + val listener = biometricManager.captureListener() + + listener.onAuthenticationStarted(REASON_AUTH_BP) + listener.onAuthenticationStopped() + assertThat(fingerprintAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + } +} + +private fun BiometricManager.captureListener() = + withArgCaptor<AuthenticationStateListener> { + verify(this@captureListener).registerAuthenticationStateListener(capture()) + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt new file mode 100644 index 000000000000..6978923879b4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt @@ -0,0 +1,173 @@ +/* + * 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.biometrics.domain.interactor + +import android.app.ActivityManager +import android.app.ActivityTaskManager +import android.content.ComponentName +import android.platform.test.annotations.RequiresFlagsEnabled +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository +import com.android.systemui.biometrics.shared.model.AuthenticationReason +import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR) +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class BiometricStatusInteractorImplTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var activityTaskManager: ActivityTaskManager + + private lateinit var biometricStatusRepository: FakeBiometricStatusRepository + private lateinit var underTest: BiometricStatusInteractorImpl + + private val testScope = TestScope(StandardTestDispatcher()) + + @Before + fun setup() { + biometricStatusRepository = FakeBiometricStatusRepository() + underTest = BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository) + } + + @Test + fun updatesSfpsAuthenticationReason_whenBiometricPromptAuthenticationStarted() = + testScope.runTest { + val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason) + runCurrent() + + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.BiometricPromptAuthentication + ) + assertThat(sfpsAuthenticationReason) + .isEqualTo(AuthenticationReason.BiometricPromptAuthentication) + } + + @Test + fun doesNotUpdateSfpsAuthenticationReason_whenDeviceEntryAuthenticationStarted() = + testScope.runTest { + val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason) + runCurrent() + + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.DeviceEntryAuthentication + ) + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + } + + @Test + fun updatesSfpsAuthenticationReason_whenOtherAuthenticationStarted() = + testScope.runTest { + val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason) + runCurrent() + + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.OtherAuthentication + ) + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.OtherAuthentication) + } + + @Test + fun doesNotUpdateSfpsAuthenticationReason_whenOtherSettingsAuthenticationStarted() = + testScope.runTest { + val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason) + runCurrent() + + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + `when`(activityTaskManager.getTasks(Mockito.anyInt())) + .thenReturn(listOf(fpSettingsTask())) + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.SettingsAuthentication(SettingsOperations.OTHER) + ) + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + } + + @Test + fun updatesSfpsAuthenticationReason_whenEnrollmentAuthenticationStarted() = + testScope.runTest { + val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason) + runCurrent() + + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_FIND_SENSOR) + ) + assertThat(sfpsAuthenticationReason) + .isEqualTo( + AuthenticationReason.SettingsAuthentication( + SettingsOperations.ENROLL_FIND_SENSOR + ) + ) + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING) + ) + assertThat(sfpsAuthenticationReason) + .isEqualTo( + AuthenticationReason.SettingsAuthentication(SettingsOperations.ENROLL_ENROLLING) + ) + } + + @Test + fun updatesFingerprintAuthenticationReason_whenAuthenticationStopped() = + testScope.runTest { + val sfpsAuthenticationReason by collectLastValue(underTest.sfpsAuthenticationReason) + runCurrent() + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.BiometricPromptAuthentication + ) + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.NotRunning + ) + assertThat(sfpsAuthenticationReason).isEqualTo(AuthenticationReason.NotRunning) + } +} + +private fun fpSettingsTask() = settingsTask(".biometrics.fingerprint.FingerprintSettings") + +private fun settingsTask(cls: String) = + ActivityManager.RunningTaskInfo().apply { + topActivity = ComponentName.createRelative("com.android.settings", cls) + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt index 22e3e7fedda0..74c43131b955 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.biometrics.shared.model +import android.hardware.fingerprint.FingerprintSensorProperties import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.faceSensorPropertiesInternal @@ -35,6 +36,46 @@ class BiometricModalitiesTest : SysuiTestCase() { } @Test + fun hasUdfps() { + with( + BiometricModalities( + fingerprintProperties = fingerprintSensorPropertiesInternal( + sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL + ).first(), + ) + ) { + assertThat(isEmpty).isFalse() + assertThat(hasUdfps).isTrue() + assertThat(hasSfps).isFalse() + assertThat(hasFace).isFalse() + assertThat(hasFaceOnly).isFalse() + assertThat(hasFingerprint).isTrue() + assertThat(hasFingerprintOnly).isTrue() + assertThat(hasFaceAndFingerprint).isFalse() + } + } + + @Test + fun hasSfps() { + with( + BiometricModalities( + fingerprintProperties = fingerprintSensorPropertiesInternal( + sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON + ).first(), + ) + ) { + assertThat(isEmpty).isFalse() + assertThat(hasUdfps).isFalse() + assertThat(hasSfps).isTrue() + assertThat(hasFace).isFalse() + assertThat(hasFaceOnly).isFalse() + assertThat(hasFingerprint).isTrue() + assertThat(hasFingerprintOnly).isTrue() + assertThat(hasFaceAndFingerprint).isFalse() + } + } + + @Test fun fingerprintOnly() { with( BiometricModalities( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt new file mode 100644 index 000000000000..b4ae00ddd608 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt @@ -0,0 +1,484 @@ +/* + * 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.biometrics.ui.binder + +import android.animation.Animator +import android.app.ActivityTaskManager +import android.graphics.Rect +import android.hardware.biometrics.SensorLocationInternal +import android.hardware.display.DisplayManager +import android.hardware.display.DisplayManagerGlobal +import android.os.Handler +import android.testing.TestableLooper +import android.view.Display +import android.view.DisplayInfo +import android.view.LayoutInflater +import android.view.View +import android.view.ViewPropertyAnimator +import android.view.WindowInsets +import android.view.WindowManager +import android.view.WindowMetrics +import androidx.test.filters.SmallTest +import com.airbnb.lottie.LottieAnimationView +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider +import com.android.systemui.biometrics.FpsUnlockTracker +import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository +import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor +import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl +import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor +import com.android.systemui.biometrics.shared.model.AuthenticationReason +import com.android.systemui.biometrics.shared.model.DisplayRotation +import com.android.systemui.biometrics.shared.model.FingerprintSensorType +import com.android.systemui.biometrics.shared.model.SensorStrength +import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.ui.BouncerView +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.display.data.repository.FakeDisplayRepository +import com.android.systemui.dump.logcatLogBuffer +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FakeTrustRepository +import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel +import com.android.systemui.log.SideFpsLogger +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.unfold.compat.ScreenSizeFoldProvider +import com.android.systemui.user.domain.interactor.SelectedUserInteractor +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock +import java.util.Optional +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.any +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class SideFpsOverlayViewBinderTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var activityTaskManager: ActivityTaskManager + @Mock private lateinit var displayManager: DisplayManager + @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock + private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider + @Mock private lateinit var fpsUnlockTracker: FpsUnlockTracker + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var layoutInflater: LayoutInflater + @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider + @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var sideFpsView: View + @Mock private lateinit var windowManager: WindowManager + + private val contextDisplayInfo = DisplayInfo() + + private val bouncerRepository = FakeKeyguardBouncerRepository() + private val biometricSettingsRepository = FakeBiometricSettingsRepository() + private val biometricStatusRepository = FakeBiometricStatusRepository() + private val deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository() + private val displayRepository = FakeDisplayRepository() + private val displayStateRepository = FakeDisplayStateRepository() + private val fingerprintPropertyRepository = FakeFingerprintPropertyRepository() + + private lateinit var underTest: SideFpsOverlayViewBinder + + private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor + private lateinit var biometricStatusInteractor: BiometricStatusInteractor + private lateinit var deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor + private lateinit var displayStateInteractor: DisplayStateInteractorImpl + private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor + private lateinit var sfpsSensorInteractor: SideFpsSensorInteractor + + private lateinit var sideFpsProgressBarViewModel: SideFpsProgressBarViewModel + + private lateinit var viewModel: SideFpsOverlayViewModel + + private var displayWidth: Int = 0 + private var displayHeight: Int = 0 + private var boundsWidth: Int = 0 + private var boundsHeight: Int = 0 + + private lateinit var deviceConfig: DeviceConfig + private lateinit var sensorLocation: SensorLocationInternal + + private val testScope = TestScope(StandardTestDispatcher()) + private val fakeExecutor = FakeExecutor(FakeSystemClock()) + + enum class DeviceConfig { + X_ALIGNED, + Y_ALIGNED, + } + + @Before + fun setup() { + mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) + + allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread + + mContext = spy(mContext) + + val resources = mContext.resources + whenever(mContext.display) + .thenReturn( + Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources) + ) + + alternateBouncerInteractor = + AlternateBouncerInteractor( + mock(StatusBarStateController::class.java), + mock(KeyguardStateController::class.java), + bouncerRepository, + fingerprintPropertyRepository, + biometricSettingsRepository, + FakeSystemClock(), + keyguardUpdateMonitor, + testScope.backgroundScope, + ) + + biometricStatusInteractor = + BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository) + + displayStateInteractor = + DisplayStateInteractorImpl( + testScope.backgroundScope, + mContext, + fakeExecutor, + displayStateRepository, + displayRepository, + ) + displayStateInteractor.setScreenSizeFoldProvider(screenSizeFoldProvider) + + primaryBouncerInteractor = + PrimaryBouncerInteractor( + bouncerRepository, + mock(BouncerView::class.java), + mock(Handler::class.java), + mock(KeyguardStateController::class.java), + mock(KeyguardSecurityModel::class.java), + mock(PrimaryBouncerCallbackInteractor::class.java), + mock(FalsingCollector::class.java), + mock(DismissCallbackRegistry::class.java), + mContext, + keyguardUpdateMonitor, + FakeTrustRepository(), + testScope.backgroundScope, + selectedUserInteractor, + faceAuthInteractor + ) + + deviceEntrySideFpsOverlayInteractor = + DeviceEntrySideFpsOverlayInteractor( + mContext, + deviceEntryFingerprintAuthRepository, + primaryBouncerInteractor, + alternateBouncerInteractor, + keyguardUpdateMonitor + ) + + whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser) + .thenReturn(MutableStateFlow(false)) + + sfpsSensorInteractor = + SideFpsSensorInteractor( + mContext, + fingerprintPropertyRepository, + windowManager, + displayStateInteractor, + Optional.of(fingerprintInteractiveToAuthProvider), + SideFpsLogger(logcatLogBuffer("SfpsLogger")) + ) + + sideFpsProgressBarViewModel = + SideFpsProgressBarViewModel( + mContext, + deviceEntryFingerprintAuthRepository, + sfpsSensorInteractor, + displayStateInteractor, + testScope.backgroundScope, + ) + + viewModel = + SideFpsOverlayViewModel( + mContext, + biometricStatusInteractor, + deviceEntrySideFpsOverlayInteractor, + displayStateInteractor, + sfpsSensorInteractor, + sideFpsProgressBarViewModel + ) + + underTest = + SideFpsOverlayViewBinder( + testScope.backgroundScope, + mContext, + biometricStatusInteractor, + displayStateInteractor, + deviceEntrySideFpsOverlayInteractor, + fpsUnlockTracker, + layoutInflater, + sideFpsProgressBarViewModel, + sfpsSensorInteractor, + windowManager + ) + + context.addMockSystemService(DisplayManager::class.java, displayManager) + context.addMockSystemService(WindowManager::class.java, windowManager) + + `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sideFpsView) + `when`(sideFpsView.requireViewById<LottieAnimationView>(eq(R.id.sidefps_animation))) + .thenReturn(mock(LottieAnimationView::class.java)) + with(mock(ViewPropertyAnimator::class.java)) { + `when`(sideFpsView.animate()).thenReturn(this) + `when`(alpha(Mockito.anyFloat())).thenReturn(this) + `when`(setStartDelay(Mockito.anyLong())).thenReturn(this) + `when`(setDuration(Mockito.anyLong())).thenReturn(this) + `when`(setListener(any())).thenAnswer { + (it.arguments[0] as Animator.AnimatorListener).onAnimationEnd( + mock(Animator::class.java) + ) + this + } + } + } + + @Test + fun verifyIndicatorNotAdded_whenInRearDisplayMode() { + testScope.runTest { + setupTestConfiguration( + DeviceConfig.X_ALIGNED, + rotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode = true + ) + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.NotRunning + ) + sideFpsProgressBarViewModel.setVisible(false) + updatePrimaryBouncer( + isShowing = true, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + runCurrent() + + verify(windowManager, never()).addView(any(), any()) + } + } + + @Test + fun verifyIndicatorShowAndHide_onPrimaryBouncerShowAndHide() { + testScope.runTest { + setupTestConfiguration( + DeviceConfig.X_ALIGNED, + rotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode = false + ) + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.NotRunning + ) + sideFpsProgressBarViewModel.setVisible(false) + // Show primary bouncer + updatePrimaryBouncer( + isShowing = true, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + runCurrent() + + verify(windowManager).addView(any(), any()) + + // Hide primary bouncer + updatePrimaryBouncer( + isShowing = false, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + runCurrent() + + verify(windowManager).removeView(any()) + } + } + + @Test + fun verifyIndicatorShowAndHide_onAlternateBouncerShowAndHide() { + testScope.runTest { + setupTestConfiguration( + DeviceConfig.X_ALIGNED, + rotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode = false + ) + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.NotRunning + ) + sideFpsProgressBarViewModel.setVisible(false) + // Show alternate bouncer + bouncerRepository.setAlternateVisible(true) + runCurrent() + + verify(windowManager).addView(any(), any()) + + // Hide alternate bouncer + bouncerRepository.setAlternateVisible(false) + runCurrent() + + verify(windowManager).removeView(any()) + } + } + + @Test + fun verifyIndicatorShownAndHidden_onSystemServerAuthenticationStartedAndStopped() { + testScope.runTest { + setupTestConfiguration( + DeviceConfig.X_ALIGNED, + rotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode = false + ) + sideFpsProgressBarViewModel.setVisible(false) + updatePrimaryBouncer( + isShowing = false, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + // System server authentication started + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.BiometricPromptAuthentication + ) + runCurrent() + + verify(windowManager).addView(any(), any()) + + // System server authentication stopped + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.NotRunning + ) + runCurrent() + + verify(windowManager).removeView(any()) + } + } + + private fun updatePrimaryBouncer( + isShowing: Boolean, + isAnimatingAway: Boolean, + fpsDetectionRunning: Boolean, + isUnlockingWithFpAllowed: Boolean, + ) { + bouncerRepository.setPrimaryShow(isShowing) + bouncerRepository.setPrimaryStartingToHide(false) + val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null + bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation) + + whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) + .thenReturn(fpsDetectionRunning) + whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) + .thenReturn(isUnlockingWithFpAllowed) + mContext.orCreateTestableResources.addOverride( + R.bool.config_show_sidefps_hint_on_bouncer, + true + ) + } + + private suspend fun TestScope.setupTestConfiguration( + deviceConfig: DeviceConfig, + rotation: DisplayRotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode: Boolean, + ) { + this@SideFpsOverlayViewBinderTest.deviceConfig = deviceConfig + + when (deviceConfig) { + DeviceConfig.X_ALIGNED -> { + displayWidth = 3000 + displayHeight = 1500 + boundsWidth = 200 + boundsHeight = 100 + sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2) + } + DeviceConfig.Y_ALIGNED -> { + displayWidth = 2500 + displayHeight = 2000 + boundsWidth = 100 + boundsHeight = 200 + sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2) + } + } + + whenever(windowManager.maximumWindowMetrics) + .thenReturn( + WindowMetrics( + Rect(0, 0, displayWidth, displayHeight), + mock(WindowInsets::class.java), + ) + ) + + contextDisplayInfo.uniqueId = DISPLAY_ID + + fingerprintPropertyRepository.setProperties( + sensorId = 1, + strength = SensorStrength.STRONG, + sensorType = FingerprintSensorType.POWER_BUTTON, + sensorLocations = mapOf(DISPLAY_ID to sensorLocation) + ) + + displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode) + displayStateRepository.setCurrentRotation(rotation) + displayRepository.emitDisplayChangeEvent(0) + underTest.start() + runCurrent() + } + + companion object { + private const val DISPLAY_ID = "displayId" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index d06cbbb5e433..7475235cfeae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.biometrics.ui.viewmodel import android.content.res.Configuration +import android.graphics.Point import android.hardware.biometrics.PromptInfo import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorProperties @@ -25,7 +26,10 @@ import android.view.HapticFeedbackConstants import android.view.MotionEvent import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils +import com.android.systemui.Flags.FLAG_BP_TALKBACK import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.AuthController +import com.android.systemui.biometrics.UdfpsUtils import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakePromptRepository @@ -33,6 +37,7 @@ import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl +import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.extractAuthenticatorTypes import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal @@ -45,8 +50,10 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.res.R -import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -77,7 +84,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @JvmField @Rule var mockitoRule = MockitoJUnit.rule() @Mock private lateinit var lockPatternUtils: LockPatternUtils - @Mock private lateinit var vibrator: VibratorHelper + @Mock private lateinit var authController: AuthController + @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var udfpsUtils: UdfpsUtils private val fakeExecutor = FakeExecutor(FakeSystemClock()) private val testScope = TestScope() @@ -87,6 +96,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private lateinit var displayStateRepository: FakeDisplayStateRepository private lateinit var displayRepository: FakeDisplayRepository private lateinit var displayStateInteractor: DisplayStateInteractor + private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor private lateinit var selector: PromptSelectorInteractor private lateinit var viewModel: PromptViewModel @@ -116,11 +126,24 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa displayStateRepository, displayRepository, ) + udfpsOverlayInteractor = + UdfpsOverlayInteractor( + authController, + selectedUserInteractor, + testScope.backgroundScope + ) selector = PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils) selector.resetPrompt() - viewModel = PromptViewModel(displayStateInteractor, selector, mContext) + viewModel = + PromptViewModel( + displayStateInteractor, + selector, + mContext, + udfpsOverlayInteractor, + udfpsUtils + ) iconViewModel = viewModel.iconViewModel } @@ -1153,6 +1176,29 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(size).isEqualTo(PromptSize.LARGE) } + @Test + fun hint_for_talkback_guidance() = runGenericTest { + mSetFlagsRule.enableFlags(FLAG_BP_TALKBACK) + val hint by collectLastValue(viewModel.accessibilityHint) + + // Touches should fall outside of sensor area + whenever(udfpsUtils.getTouchInNativeCoordinates(any(), any(), any())) + .thenReturn(Point(0, 0)) + whenever(udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any())) + .thenReturn("Direction") + + viewModel.onAnnounceAccessibilityHint( + obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER), + true + ) + + if (testCase.modalities.hasUdfps) { + assertThat(hint?.isNotBlank()).isTrue() + } else { + assertThat(hint.isNullOrBlank()).isTrue() + } + } + /** Asserts that the selected buttons are visible now. */ private suspend fun TestScope.assertButtonsVisible( tryAgain: Boolean = false, @@ -1220,14 +1266,19 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa authenticatedModality = BiometricModality.Face, ), TestCase( - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), + fingerprint = + fingerprintSensorPropertiesInternal( + strong = true, + sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON + ) + .first(), authenticatedModality = BiometricModality.Fingerprint, ), TestCase( fingerprint = fingerprintSensorPropertiesInternal( strong = true, - sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON + sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL ) .first(), authenticatedModality = BiometricModality.Fingerprint, @@ -1264,19 +1315,29 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa TestCase( face = faceSensorPropertiesInternal(strong = true).first(), fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Fingerprint, + authenticatedModality = BiometricModality.Face, + confirmationRequested = true, ), TestCase( face = faceSensorPropertiesInternal(strong = true).first(), - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Face, + fingerprint = + fingerprintSensorPropertiesInternal( + strong = true, + sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON + ) + .first(), + authenticatedModality = BiometricModality.Fingerprint, confirmationRequested = true, ), TestCase( face = faceSensorPropertiesInternal(strong = true).first(), - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), + fingerprint = + fingerprintSensorPropertiesInternal( + strong = true, + sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL + ) + .first(), authenticatedModality = BiometricModality.Fingerprint, - confirmationRequested = true, ), ) } @@ -1309,6 +1370,9 @@ internal data class TestCase( else -> false } + val modalities: BiometricModalities + get() = BiometricModalities(fingerprint, face) + val authenticatedByFingerprint: Boolean get() = authenticatedModality == BiometricModality.Fingerprint diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt new file mode 100644 index 000000000000..2267bdcafad8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt @@ -0,0 +1,576 @@ +/* + * 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.biometrics.ui.viewmodel + +import android.app.ActivityTaskManager +import android.content.res.Configuration.UI_MODE_NIGHT_NO +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import android.graphics.Color +import android.graphics.Rect +import android.hardware.biometrics.SensorLocationInternal +import android.hardware.display.DisplayManagerGlobal +import android.os.Handler +import android.view.Display +import android.view.DisplayInfo +import android.view.WindowInsets +import android.view.WindowManager +import android.view.WindowMetrics +import androidx.test.filters.SmallTest +import com.airbnb.lottie.model.KeyPath +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.settingslib.Utils +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider +import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository +import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor +import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl +import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl +import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor +import com.android.systemui.biometrics.shared.model.AuthenticationReason +import com.android.systemui.biometrics.shared.model.DisplayRotation +import com.android.systemui.biometrics.shared.model.FingerprintSensorType +import com.android.systemui.biometrics.shared.model.LottieCallback +import com.android.systemui.biometrics.shared.model.SensorStrength +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.ui.BouncerView +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.display.data.repository.FakeDisplayRepository +import com.android.systemui.dump.logcatLogBuffer +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FakeTrustRepository +import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor +import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel +import com.android.systemui.log.SideFpsLogger +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.unfold.compat.ScreenSizeFoldProvider +import com.android.systemui.user.domain.interactor.SelectedUserInteractor +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import java.util.Optional +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.spy +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class SideFpsOverlayViewModelTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var activityTaskManager: ActivityTaskManager + @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock + private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider + @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var windowManager: WindowManager + + private val contextDisplayInfo = DisplayInfo() + + private val bouncerRepository = FakeKeyguardBouncerRepository() + private val biometricSettingsRepository = FakeBiometricSettingsRepository() + private val biometricStatusRepository = FakeBiometricStatusRepository() + private val deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository() + private val displayRepository = FakeDisplayRepository() + private val displayStateRepository = FakeDisplayStateRepository() + private val fingerprintPropertyRepository = FakeFingerprintPropertyRepository() + + private val indicatorColor = + Utils.getColorAttrDefaultColor( + context, + com.android.internal.R.attr.materialColorPrimaryFixed + ) + private val outerRimColor = + Utils.getColorAttrDefaultColor( + context, + com.android.internal.R.attr.materialColorPrimaryFixedDim + ) + private val chevronFill = + Utils.getColorAttrDefaultColor( + context, + com.android.internal.R.attr.materialColorOnPrimaryFixed + ) + private val color_blue400 = + context.getColor(com.android.settingslib.color.R.color.settingslib_color_blue400) + + private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor + private lateinit var biometricStatusInteractor: BiometricStatusInteractor + private lateinit var deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor + private lateinit var displayStateInteractor: DisplayStateInteractorImpl + private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor + private lateinit var sfpsSensorInteractor: SideFpsSensorInteractor + + private lateinit var sideFpsProgressBarViewModel: SideFpsProgressBarViewModel + + private lateinit var underTest: SideFpsOverlayViewModel + + private var displayWidth: Int = 0 + private var displayHeight: Int = 0 + private var boundsWidth: Int = 0 + private var boundsHeight: Int = 0 + + private lateinit var deviceConfig: DeviceConfig + private lateinit var sensorLocation: SensorLocationInternal + + private val testScope = TestScope(StandardTestDispatcher()) + private val fakeExecutor = FakeExecutor(FakeSystemClock()) + + enum class DeviceConfig { + X_ALIGNED, + Y_ALIGNED, + } + + @Before + fun setup() { + mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) + + mContext = spy(mContext) + + val resources = mContext.resources + whenever(mContext.display) + .thenReturn( + Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources) + ) + + alternateBouncerInteractor = + AlternateBouncerInteractor( + mock(StatusBarStateController::class.java), + mock(KeyguardStateController::class.java), + bouncerRepository, + fingerprintPropertyRepository, + biometricSettingsRepository, + FakeSystemClock(), + keyguardUpdateMonitor, + testScope.backgroundScope, + ) + + biometricStatusInteractor = + BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository) + + displayStateInteractor = + DisplayStateInteractorImpl( + testScope.backgroundScope, + mContext, + fakeExecutor, + displayStateRepository, + displayRepository, + ) + displayStateInteractor.setScreenSizeFoldProvider(screenSizeFoldProvider) + + primaryBouncerInteractor = + PrimaryBouncerInteractor( + bouncerRepository, + mock(BouncerView::class.java), + mock(Handler::class.java), + mock(KeyguardStateController::class.java), + mock(KeyguardSecurityModel::class.java), + mock(PrimaryBouncerCallbackInteractor::class.java), + mock(FalsingCollector::class.java), + mock(DismissCallbackRegistry::class.java), + mContext, + keyguardUpdateMonitor, + FakeTrustRepository(), + testScope.backgroundScope, + selectedUserInteractor, + faceAuthInteractor + ) + + deviceEntrySideFpsOverlayInteractor = + DeviceEntrySideFpsOverlayInteractor( + mContext, + deviceEntryFingerprintAuthRepository, + primaryBouncerInteractor, + alternateBouncerInteractor, + keyguardUpdateMonitor + ) + + whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser) + .thenReturn(MutableStateFlow(false)) + + sfpsSensorInteractor = + SideFpsSensorInteractor( + mContext, + fingerprintPropertyRepository, + windowManager, + displayStateInteractor, + Optional.of(fingerprintInteractiveToAuthProvider), + SideFpsLogger(logcatLogBuffer("SfpsLogger")) + ) + + sideFpsProgressBarViewModel = + SideFpsProgressBarViewModel( + mContext, + deviceEntryFingerprintAuthRepository, + sfpsSensorInteractor, + displayStateInteractor, + testScope.backgroundScope, + ) + + underTest = + SideFpsOverlayViewModel( + mContext, + biometricStatusInteractor, + deviceEntrySideFpsOverlayInteractor, + displayStateInteractor, + sfpsSensorInteractor, + sideFpsProgressBarViewModel, + ) + } + + @Test + fun updatesOverlayViewProperties_onDisplayRotationChange_xAlignedSensor() { + testScope.runTest { + setupTestConfiguration( + DeviceConfig.X_ALIGNED, + rotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode = false + ) + + val overlayViewProperties by collectLastValue(underTest.overlayViewProperties) + + runCurrent() + + assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape) + assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + + assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse) + assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) + + assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape) + assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + + assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse) + assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f) + } + } + + @Test + fun updatesOverlayViewProperties_onDisplayRotationChange_yAlignedSensor() { + testScope.runTest { + setupTestConfiguration( + DeviceConfig.Y_ALIGNED, + rotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode = false + ) + + val overlayViewProperties by collectLastValue(underTest.overlayViewProperties) + + runCurrent() + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) + assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse) + assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + + assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape) + assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) + assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse) + assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + + assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape) + assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f) + } + } + + @Test + fun updatesOverlayViewParams_onDisplayRotationChange_xAlignedSensor() { + testScope.runTest { + setupTestConfiguration( + DeviceConfig.X_ALIGNED, + rotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode = false + ) + + val overlayViewParams by collectLastValue(underTest.overlayViewParams) + + underTest.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight)) + runCurrent() + + assertThat(overlayViewParams).isNotNull() + assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationX) + assertThat(overlayViewParams!!.y).isEqualTo(0) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + assertThat(overlayViewParams).isNotNull() + assertThat(overlayViewParams!!.x).isEqualTo(0) + assertThat(overlayViewParams!!.y) + .isEqualTo( + displayHeight - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2 + ) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) + assertThat(overlayViewParams).isNotNull() + assertThat(overlayViewParams!!.x) + .isEqualTo( + displayWidth - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2 + ) + assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + assertThat(overlayViewParams).isNotNull() + assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth) + assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationX) + } + } + + @Test + fun updatesOverlayViewParams_onDisplayRotationChange_yAlignedSensor() { + testScope.runTest { + setupTestConfiguration( + DeviceConfig.Y_ALIGNED, + rotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode = false + ) + + val overlayViewParams by collectLastValue(underTest.overlayViewParams) + + underTest.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight)) + runCurrent() + + assertThat(overlayViewParams).isNotNull() + assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth) + assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationY) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + assertThat(overlayViewParams).isNotNull() + assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationY) + assertThat(overlayViewParams!!.y).isEqualTo(0) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) + assertThat(overlayViewParams).isNotNull() + assertThat(overlayViewParams!!.x).isEqualTo(0) + assertThat(overlayViewParams!!.y) + .isEqualTo( + displayHeight - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2 + ) + + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + assertThat(overlayViewParams).isNotNull() + assertThat(overlayViewParams!!.x) + .isEqualTo( + displayWidth - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2 + ) + assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight) + } + } + + @Test + fun updatesLottieCallbacks_onShowIndicatorForDeviceEntry() { + testScope.runTest { + val lottieCallbacks by collectLastValue(underTest.lottieCallbacks) + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.NotRunning + ) + sideFpsProgressBarViewModel.setVisible(false) + + updatePrimaryBouncer( + isShowing = true, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + runCurrent() + + assertThat(lottieCallbacks) + .contains(LottieCallback(KeyPath(".blue600", "**"), indicatorColor)) + assertThat(lottieCallbacks) + .contains(LottieCallback(KeyPath(".blue400", "**"), outerRimColor)) + assertThat(lottieCallbacks) + .contains(LottieCallback(KeyPath(".black", "**"), chevronFill)) + } + } + + @Test + fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inDarkMode() { + testScope.runTest { + val lottieCallbacks by collectLastValue(underTest.lottieCallbacks) + setDarkMode(true) + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.BiometricPromptAuthentication + ) + sideFpsProgressBarViewModel.setVisible(false) + + updatePrimaryBouncer( + isShowing = false, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + runCurrent() + + assertThat(lottieCallbacks) + .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400)) + assertThat(lottieCallbacks) + .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400)) + } + } + + @Test + fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inLightMode() { + testScope.runTest { + val lottieCallbacks by collectLastValue(underTest.lottieCallbacks) + setDarkMode(false) + + biometricStatusRepository.setFingerprintAuthenticationReason( + AuthenticationReason.BiometricPromptAuthentication + ) + sideFpsProgressBarViewModel.setVisible(false) + + updatePrimaryBouncer( + isShowing = false, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + runCurrent() + + assertThat(lottieCallbacks) + .contains(LottieCallback(KeyPath(".black", "**"), Color.WHITE)) + assertThat(lottieCallbacks) + .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400)) + assertThat(lottieCallbacks) + .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400)) + } + } + + private fun setDarkMode(inDarkMode: Boolean) { + val uiMode = + if (inDarkMode) { + UI_MODE_NIGHT_YES + } else { + UI_MODE_NIGHT_NO + } + + mContext.resources.configuration.uiMode = uiMode + } + + private fun updatePrimaryBouncer( + isShowing: Boolean, + isAnimatingAway: Boolean, + fpsDetectionRunning: Boolean, + isUnlockingWithFpAllowed: Boolean, + ) { + bouncerRepository.setPrimaryShow(isShowing) + bouncerRepository.setPrimaryStartingToHide(false) + val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null + bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation) + + whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) + .thenReturn(fpsDetectionRunning) + whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) + .thenReturn(isUnlockingWithFpAllowed) + mContext.orCreateTestableResources.addOverride( + R.bool.config_show_sidefps_hint_on_bouncer, + true + ) + } + + private suspend fun TestScope.setupTestConfiguration( + deviceConfig: DeviceConfig, + rotation: DisplayRotation = DisplayRotation.ROTATION_0, + isInRearDisplayMode: Boolean, + ) { + this@SideFpsOverlayViewModelTest.deviceConfig = deviceConfig + + when (deviceConfig) { + DeviceConfig.X_ALIGNED -> { + displayWidth = 3000 + displayHeight = 1500 + boundsWidth = 200 + boundsHeight = 100 + sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2) + } + DeviceConfig.Y_ALIGNED -> { + displayWidth = 2500 + displayHeight = 2000 + boundsWidth = 100 + boundsHeight = 200 + sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2) + } + } + + whenever(windowManager.maximumWindowMetrics) + .thenReturn( + WindowMetrics( + Rect(0, 0, displayWidth, displayHeight), + mock(WindowInsets::class.java), + ) + ) + + contextDisplayInfo.uniqueId = DISPLAY_ID + + fingerprintPropertyRepository.setProperties( + sensorId = 1, + strength = SensorStrength.STRONG, + sensorType = FingerprintSensorType.POWER_BUTTON, + sensorLocations = mapOf(DISPLAY_ID to sensorLocation) + ) + + displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode) + + displayStateRepository.setCurrentRotation(rotation) + + displayRepository.emitDisplayChangeEvent(0) + runCurrent() + } + + companion object { + private const val DISPLAY_ID = "displayId" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt index 37a093e4c23f..dacf23a5a567 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -37,6 +37,7 @@ import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.mockito.any @@ -342,6 +343,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { assertThat(underTest.willDismissWithAction()).isFalse() } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun testSideFpsVisibility() { updateSideFpsVisibilityParameters( @@ -355,6 +357,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { verify(repository).setSideFpsShowing(true) } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun testSideFpsVisibility_notVisible() { updateSideFpsVisibilityParameters( @@ -368,6 +371,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { verify(repository).setSideFpsShowing(false) } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun testSideFpsVisibility_sfpsNotEnabled() { updateSideFpsVisibilityParameters( @@ -381,6 +385,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { verify(repository).setSideFpsShowing(false) } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun testSideFpsVisibility_fpsDetectionNotRunning() { updateSideFpsVisibilityParameters( @@ -394,6 +399,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { verify(repository).setSideFpsShowing(false) } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun testSideFpsVisibility_UnlockingWithFpNotAllowed() { updateSideFpsVisibilityParameters( @@ -407,6 +413,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { verify(repository).setSideFpsShowing(false) } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Test fun testSideFpsVisibility_AnimatingAway() { updateSideFpsVisibilityParameters( @@ -492,6 +499,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { isUnlockingWithFpAllowed: Boolean, isAnimatingAway: Boolean ) { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR) whenever(repository.primaryBouncerShow.value).thenReturn(isVisible) resources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, sfpsEnabled) whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java index 220718027eee..07c980bb6656 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java @@ -21,14 +21,13 @@ import static com.android.systemui.controls.dagger.ControlsComponent.Visibility. import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ComponentName; -import android.content.Context; import android.content.res.Resources; import android.testing.AndroidTestingRunner; import android.view.View; @@ -48,6 +47,7 @@ import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Before; import org.junit.Test; @@ -71,9 +71,6 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { private DreamOverlayStateController mDreamOverlayStateController; @Mock - private Context mContext; - - @Mock private Resources mResources; @Mock @@ -100,6 +97,9 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { @Mock private UiEventLogger mUiEventLogger; + @Mock + private ConfigurationController mConfigurationController; + @Captor private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor; @@ -109,7 +109,8 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); - when(mContext.getString(anyInt())).thenReturn(""); + mContext.ensureTestableResources(); + when(mControlsComponent.getControlsController()).thenReturn( Optional.of(mControlsController)); when(mControlsComponent.getControlsListingController()).thenReturn( @@ -225,6 +226,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { mHomeControlsView, mActivityStarter, mContext, + mConfigurationController, mControlsComponent, mUiEventLogger); viewController.onViewAttached(); @@ -237,6 +239,24 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { verify(mUiEventLogger).log(DreamOverlayUiEvent.DREAM_HOME_CONTROLS_TAPPED); } + @Test + public void testUnregistersConfigurationCallback() { + final DreamHomeControlsComplication.DreamHomeControlsChipViewController viewController = + new DreamHomeControlsComplication.DreamHomeControlsChipViewController( + mHomeControlsView, + mActivityStarter, + mContext, + mConfigurationController, + mControlsComponent, + mUiEventLogger); + viewController.onViewAttached(); + verify(mConfigurationController).addCallback(any()); + verify(mConfigurationController, never()).removeCallback(any()); + + viewController.onViewDetached(); + verify(mConfigurationController).removeCallback(any()); + } + private void setHaveFavorites(boolean value) { final List<StructureInfo> favorites = mock(List.class); when(favorites.isEmpty()).thenReturn(!value); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 4ef18cf1a15f..b38c9ecb6287 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -24,6 +24,7 @@ import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; +import static com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER; import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION; import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT; import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE; @@ -266,7 +267,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mSceneContainerFlags); mFeatureFlags = new FakeFeatureFlags(); mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false); - mFeatureFlags.set(Flags.REFACTOR_GETCURRENTUSER, true); + mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER); DejankUtils.setImmediate(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt new file mode 100644 index 000000000000..70d3f81de35f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.os.Handler +import android.platform.test.annotations.RequiresFlagsEnabled +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.ui.BouncerView +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.FakeTrustRepository +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R +import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.user.domain.interactor.SelectedUserInteractor +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@OptIn(ExperimentalCoroutinesApi::class) +@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR) +@SmallTest +@RunWith(JUnit4::class) +class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor + + private val bouncerRepository = FakeKeyguardBouncerRepository() + private val biometricSettingsRepository = FakeBiometricSettingsRepository() + + private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor + private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor + + private lateinit var underTest: DeviceEntrySideFpsOverlayInteractor + + private val testScope = TestScope(StandardTestDispatcher()) + + @Before + fun setup() { + primaryBouncerInteractor = + PrimaryBouncerInteractor( + bouncerRepository, + mock(BouncerView::class.java), + mock(Handler::class.java), + mock(KeyguardStateController::class.java), + mock(KeyguardSecurityModel::class.java), + mock(PrimaryBouncerCallbackInteractor::class.java), + mock(FalsingCollector::class.java), + mock(DismissCallbackRegistry::class.java), + mContext, + keyguardUpdateMonitor, + FakeTrustRepository(), + testScope.backgroundScope, + mSelectedUserInteractor, + faceAuthInteractor + ) + alternateBouncerInteractor = + AlternateBouncerInteractor( + mock(StatusBarStateController::class.java), + mock(KeyguardStateController::class.java), + bouncerRepository, + FakeFingerprintPropertyRepository(), + biometricSettingsRepository, + FakeSystemClock(), + keyguardUpdateMonitor, + testScope.backgroundScope, + ) + underTest = + DeviceEntrySideFpsOverlayInteractor( + mContext, + FakeDeviceEntryFingerprintAuthRepository(), + primaryBouncerInteractor, + alternateBouncerInteractor, + keyguardUpdateMonitor + ) + } + + @Test + fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerShowing() = + testScope.runTest { + val showIndicatorForDeviceEntry by + collectLastValue(underTest.showIndicatorForDeviceEntry) + runCurrent() + + updatePrimaryBouncer( + isShowing = true, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + assertThat(showIndicatorForDeviceEntry).isEqualTo(true) + } + + @Test + fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerHidden() = + testScope.runTest { + val showIndicatorForDeviceEntry by + collectLastValue(underTest.showIndicatorForDeviceEntry) + runCurrent() + + updatePrimaryBouncer( + isShowing = false, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + assertThat(showIndicatorForDeviceEntry).isEqualTo(false) + } + + @Test + fun updatesShowIndicatorForDeviceEntry_fromPrimaryBouncer_whenFpsDetectionNotRunning() { + testScope.runTest { + val showIndicatorForDeviceEntry by + collectLastValue(underTest.showIndicatorForDeviceEntry) + runCurrent() + + updatePrimaryBouncer( + isShowing = true, + isAnimatingAway = false, + fpsDetectionRunning = false, + isUnlockingWithFpAllowed = true + ) + assertThat(showIndicatorForDeviceEntry).isEqualTo(false) + } + } + + @Test + fun updatesShowIndicatorForDeviceEntry_fromPrimaryBouncer_onUnlockingWithFpDisallowed() { + testScope.runTest { + val showIndicatorForDeviceEntry by + collectLastValue(underTest.showIndicatorForDeviceEntry) + runCurrent() + + updatePrimaryBouncer( + isShowing = true, + isAnimatingAway = false, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = false + ) + assertThat(showIndicatorForDeviceEntry).isEqualTo(false) + } + } + + @Test + fun updatesShowIndicatorForDeviceEntry_onPrimaryBouncerAnimatingAway() { + testScope.runTest { + val showIndicatorForDeviceEntry by + collectLastValue(underTest.showIndicatorForDeviceEntry) + runCurrent() + + updatePrimaryBouncer( + isShowing = true, + isAnimatingAway = true, + fpsDetectionRunning = true, + isUnlockingWithFpAllowed = true + ) + assertThat(showIndicatorForDeviceEntry).isEqualTo(false) + } + } + + @Test + fun updatesShowIndicatorForDeviceEntry_onAlternateBouncerRequest() = + testScope.runTest { + val showIndicatorForDeviceEntry by + collectLastValue(underTest.showIndicatorForDeviceEntry) + runCurrent() + + bouncerRepository.setAlternateVisible(true) + assertThat(showIndicatorForDeviceEntry).isEqualTo(true) + + bouncerRepository.setAlternateVisible(false) + assertThat(showIndicatorForDeviceEntry).isEqualTo(false) + } + + private fun updatePrimaryBouncer( + isShowing: Boolean, + isAnimatingAway: Boolean, + fpsDetectionRunning: Boolean, + isUnlockingWithFpAllowed: Boolean, + ) { + bouncerRepository.setPrimaryShow(isShowing) + bouncerRepository.setPrimaryStartingToHide(false) + val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null + bouncerRepository.setPrimaryStartDisappearAnimation(primaryStartDisappearAnimation) + + whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) + .thenReturn(fpsDetectionRunning) + whenever(keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) + .thenReturn(isUnlockingWithFpAllowed) + mContext.orCreateTestableResources.addOverride( + R.bool.config_show_sidefps_hint_on_bouncer, + true + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt index 8dea57c2eb34..8e81185d6dcf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt @@ -20,8 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.FakeFeatureFlagsClassic -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -46,11 +44,7 @@ import org.junit.runner.RunWith class FromPrimaryBouncerTransitionInteractorTest : KeyguardTransitionInteractorTestCase() { private lateinit var underTest: FromPrimaryBouncerTransitionInteractor - private val mSelectedUserInteractor = - SelectedUserInteractor( - FakeUserRepository(), - FakeFeatureFlagsClassic().apply { set(Flags.REFACTOR_GETCURRENTUSER, true) } - ) + private val mSelectedUserInteractor = SelectedUserInteractor(FakeUserRepository()) // Override the fromPrimaryBouncerTransitionInteractor provider from the superclass so our // underTest interactor is provided to any classes that need it. diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index b7618d290f53..4d423242893a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -75,6 +75,7 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt @@ -462,6 +463,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { // region setNoteTaskShortcutEnabled @Test + @Ignore("b/316332684") fun setNoteTaskShortcutEnabled_setTrue() { createNoteTaskController().setNoteTaskShortcutEnabled(value = true, userTracker.userHandle) @@ -478,6 +480,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test + @Ignore("b/316332684") fun setNoteTaskShortcutEnabled_setFalse() { createNoteTaskController().setNoteTaskShortcutEnabled(value = false, userTracker.userHandle) @@ -494,6 +497,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test + @Ignore("b/316332684") fun setNoteTaskShortcutEnabled_workProfileUser_setTrue() { whenever(context.createContextAsUser(eq(workUserInfo.userHandle), any())) .thenReturn(workProfileContext) @@ -515,6 +519,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test + @Ignore("b/316332684") fun setNoteTaskShortcutEnabled_workProfileUser_setFalse() { whenever(context.createContextAsUser(eq(workUserInfo.userHandle), any())) .thenReturn(workProfileContext) @@ -733,6 +738,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { // region internalUpdateNoteTaskAsUser @Test + @Ignore("b/316332684") fun updateNoteTaskAsUserInternal_withNotesRole_withShortcuts_shouldUpdateShortcuts() { createNoteTaskController(isEnabled = true) .launchUpdateNoteTaskAsUser(userTracker.userHandle) @@ -766,6 +772,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test + @Ignore("b/316332684") fun updateNoteTaskAsUserInternal_noNotesRole_shouldDisableShortcuts() { whenever(roleManager.getRoleHoldersAsUser(ROLE_NOTES, userTracker.userHandle)) .thenReturn(emptyList()) @@ -789,6 +796,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test + @Ignore("b/316332684") fun updateNoteTaskAsUserInternal_flagDisabled_shouldDisableShortcuts() { createNoteTaskController(isEnabled = false) .launchUpdateNoteTaskAsUser(userTracker.userHandle) diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt index 119ffd28bebe..ebd34de463f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt @@ -28,7 +28,6 @@ import android.os.UserManager import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import com.android.dx.mockito.inline.extended.ExtendedMockito -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon @@ -40,6 +39,7 @@ import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.C import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint import com.android.systemui.notetask.NoteTaskInfoResolver +import com.android.systemui.res.R import com.android.systemui.stylus.StylusManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -134,12 +134,9 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { // region lockScreenState @Test fun lockScreenState_stylusUsed_userUnlocked_isSelected_shouldEmitVisible() = runTest { - TestConfig() - .setStylusEverUsed(true) - .setUserUnlocked(true) - .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) - val underTest = createUnderTest() + TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(underTest) + val actual by collectLastValue(underTest.lockScreenState) assertThat(actual).isEqualTo(createLockScreenStateVisible()) @@ -148,10 +145,11 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { @Test fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() = runTest { + val underTest = createUnderTest() TestConfig() .setStylusEverUsed(true) .setUserUnlocked(true) - .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) + .setConfigSelections(underTest) whenever( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_NOTES), @@ -160,7 +158,6 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { ) .thenReturn(emptyList()) - val underTest = createUnderTest() val actual by collectLastValue(underTest.lockScreenState) assertThat(actual).isEqualTo(LockScreenState.Hidden) @@ -168,12 +165,9 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { @Test fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest { - TestConfig() - .setStylusEverUsed(false) - .setUserUnlocked(true) - .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) - val underTest = createUnderTest() + TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(underTest) + val actual by collectLastValue(underTest.lockScreenState) assertThat(actual).isEqualTo(LockScreenState.Hidden) @@ -181,25 +175,22 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { @Test fun lockScreenState_stylusUsed_userLocked_isSelected_shouldEmitHidden() = runTest { - TestConfig() - .setStylusEverUsed(true) - .setUserUnlocked(false) - .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) - val underTest = createUnderTest() + TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(underTest) + val actual by collectLastValue(underTest.lockScreenState) assertThat(actual).isEqualTo(LockScreenState.Hidden) } @Test - fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitVisible() = runTest { + fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitHidden() = runTest { TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections() val underTest = createUnderTest() val actual by collectLastValue(underTest.lockScreenState) - assertThat(actual).isEqualTo(createLockScreenStateVisible()) + assertThat(actual).isEqualTo(LockScreenState.Hidden) } @Test @@ -223,13 +214,13 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitVisible() = runTest { + fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitHidden() = runTest { TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(mock()) val underTest = createUnderTest() val actual by collectLastValue(underTest.lockScreenState) - assertThat(actual).isEqualTo(createLockScreenStateVisible()) + assertThat(actual).isEqualTo(LockScreenState.Hidden) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index b825c0840e01..018fa9e2bc86 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -391,7 +391,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { } @Test - public void setTiles_differentTiles_allTilesRemovedAndNewTilesAdded() { + public void setTiles_differentTiles_extraTileRemoved() { when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); mController.setTiles(); @@ -400,8 +400,8 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { when(mQSHost.getTiles()).thenReturn(List.of(mQSTile)); mController.setTiles(); - verify(mQSPanel, times(2)).removeTile(any()); - verify(mQSPanel).addTile(any()); + verify(mQSPanel, times(1)).removeTile(any()); + verify(mQSPanel, never()).addTile(any()); } @Test @@ -418,7 +418,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { } @Test - public void setTiles_sameTilesDifferentOrder_removesAndReadds() { + public void setTiles_sameTilesDifferentOrder_removesAndReads() { when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile)); mController.setTiles(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt index e9714dc524ee..9b61447c8de7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.res.R +import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController @@ -66,6 +67,7 @@ class RecordIssueTileTest : SysuiTestCase() { @Mock private lateinit var dialogLauncherAnimator: DialogLaunchAnimator @Mock private lateinit var dialogFactory: SystemUIDialog.Factory @Mock private lateinit var dialog: SystemUIDialog + @Mock private lateinit var userContextProvider: UserContextProvider private lateinit var testableLooper: TestableLooper private lateinit var tile: RecordIssueTile @@ -91,7 +93,8 @@ class RecordIssueTileTest : SysuiTestCase() { keyguardDismissUtil, keyguardStateController, dialogLauncherAnimator, - dialogFactory + dialogFactory, + userContextProvider, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt index bbc59d03ac2b..c5d35245ba7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt @@ -65,7 +65,8 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { ) latch = CountDownLatch(1) - dialog = RecordIssueDialogDelegate(dialogFactory) { latch.countDown() }.createDialog() + dialog = + RecordIssueDialogDelegate(dialogFactory, mock()) { latch.countDown() }.createDialog() dialog.show() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index 112368895888..b58a41c89a4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.theme; +import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; @@ -90,6 +91,9 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { private static final int USER_SYSTEM = UserHandle.USER_SYSTEM; private static final int USER_SECONDARY = 10; + private static final UserHandle MANAGED_USER_HANDLE = UserHandle.of(100); + private static final UserHandle PRIVATE_USER_HANDLE = UserHandle.of(101); + @Mock private JavaAdapter mJavaAdapter; @Mock @@ -174,6 +178,14 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { Integer.toHexString(mColorScheme.getSeed() | 0xff000000))); return overlay; } + + @VisibleForTesting + protected boolean isPrivateProfile(UserHandle userHandle) { + if (userHandle.getIdentifier() == PRIVATE_USER_HANDLE.getIdentifier()) { + return true; + } + return false; + } }; mWakefulnessLifecycle.dispatchFinishedWakingUp(); @@ -675,7 +687,8 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { @Test public void onProfileAdded_setsTheme() { mBroadcastReceiver.getValue().onReceive(null, - new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED)); + new Intent(Intent.ACTION_PROFILE_ADDED) + .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); } @@ -684,7 +697,8 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { reset(mDeviceProvisionedController); when(mUserManager.isManagedProfile(anyInt())).thenReturn(false); mBroadcastReceiver.getValue().onReceive(null, - new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED)); + new Intent(Intent.ACTION_PROFILE_ADDED) + .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); verify(mThemeOverlayApplier) .applyCurrentUserOverlays(any(), any(), anyInt(), any()); } @@ -694,11 +708,25 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { reset(mDeviceProvisionedController); when(mUserManager.isManagedProfile(anyInt())).thenReturn(true); mBroadcastReceiver.getValue().onReceive(null, - new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED)); + new Intent(Intent.ACTION_PROFILE_ADDED) + .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); + verify(mThemeOverlayApplier, never()) + .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + } + + @Test + public void onPrivateProfileAdded_ignoresUntilStartComplete() { + mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE); + reset(mDeviceProvisionedController); + when(mUserManager.isManagedProfile(anyInt())).thenReturn(false); + mBroadcastReceiver.getValue().onReceive(null, + (new Intent(Intent.ACTION_PROFILE_ADDED)) + .putExtra(Intent.EXTRA_USER, PRIVATE_USER_HANDLE)); verify(mThemeOverlayApplier, never()) .applyCurrentUserOverlays(any(), any(), anyInt(), any()); } + @Test public void onWallpaperColorsChanged_firstEventBeforeUserSetup_shouldBeAccepted() { // By default, on setup() we make this controller return that the user finished setup diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt index 60fe7d2248e4..140e919d613f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt @@ -2,9 +2,8 @@ package com.android.systemui.user.domain.interactor import android.content.pm.UserInfo import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlagsClassic -import com.android.systemui.flags.Flags import com.android.systemui.user.data.repository.FakeUserRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.runBlocking @@ -24,15 +23,12 @@ class SelectedUserInteractorTest : SysuiTestCase() { @Before fun setUp() { userRepository.setUserInfos(USER_INFOS) - underTest = - SelectedUserInteractor( - userRepository, - FakeFeatureFlagsClassic().apply { set(Flags.REFACTOR_GETCURRENTUSER, true) } - ) + underTest = SelectedUserInteractor(userRepository) } @Test fun getSelectedUserIdReturnsId() { + mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER) runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) } val actualId = underTest.getSelectedUserId() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt new file mode 100644 index 000000000000..607a4f3e1c0f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt @@ -0,0 +1,39 @@ +/* + * 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.accessibility.data.repository + +import android.os.UserHandle +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class FakeColorCorrectionRepository : ColorCorrectionRepository { + private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>() + + override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> { + return getFlow(userHandle.identifier) + } + + override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean { + getFlow(userHandle.identifier).value = isEnabled + return true + } + + /** initializes the flow if already not */ + private fun getFlow(userId: Int): MutableStateFlow<Boolean> { + return userMap.getOrPut(userId) { MutableStateFlow(false) } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt new file mode 100644 index 000000000000..1c8bd3b58dfe --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeBiometricStatusRepository.kt @@ -0,0 +1,34 @@ +/* + * 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.biometrics.data.repository + +import com.android.systemui.biometrics.shared.model.AuthenticationReason +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeBiometricStatusRepository : BiometricStatusRepository { + private val _fingerprintAuthenticationReason = + MutableStateFlow<AuthenticationReason>(AuthenticationReason.NotRunning) + override val fingerprintAuthenticationReason: StateFlow<AuthenticationReason> = + _fingerprintAuthenticationReason.asStateFlow() + + fun setFingerprintAuthenticationReason(reason: AuthenticationReason) { + _fingerprintAuthenticationReason.value = reason + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt index f84481c55664..ff5179a7042c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt @@ -1,5 +1,6 @@ package com.android.systemui.bouncer.data.repository +import com.android.systemui.biometrics.shared.SideFpsControllerRefactor import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import com.android.systemui.dagger.SysUISingleton @@ -113,7 +114,9 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos _isBackButtonEnabled.value = isBackButtonEnabled } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR override fun setSideFpsShowing(isShowing: Boolean) { + SideFpsControllerRefactor.assertInLegacyMode() _sideFpsShowing.value = isShowing } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt index c9160efb75a4..1d44929a20f0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt @@ -54,6 +54,11 @@ class FakeDeviceEntryFingerprintAuthRepository @Inject constructor() : private var _authenticationStatus = MutableStateFlow<FingerprintAuthenticationStatus?>(null) override val authenticationStatus: Flow<FingerprintAuthenticationStatus> get() = _authenticationStatus.filterNotNull() + + private var _shouldUpdateIndicatorVisibility = MutableStateFlow(false) + override val shouldUpdateIndicatorVisibility: Flow<Boolean> + get() = _shouldUpdateIndicatorVisibility + fun setAuthenticationStatus(status: FingerprintAuthenticationStatus) { _authenticationStatus.value = status } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt index 3d8ae1e9801a..3cabf0c07423 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt @@ -27,8 +27,6 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInte import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector -import com.android.systemui.flags.FakeFeatureFlagsClassic -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -57,12 +55,6 @@ object KeyguardDismissInteractorFactory { keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(), bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(), keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(KeyguardUpdateMonitor::class.java), - featureFlags: FakeFeatureFlagsClassic = - FakeFeatureFlagsClassic().apply { - set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, true) - set(Flags.FULL_SCREEN_USER_SWITCHER, false) - set(Flags.REFACTOR_GETCURRENTUSER, true) - }, powerRepository: FakePowerRepository = FakePowerRepository(), userRepository: FakeUserRepository = FakeUserRepository(), ): WithDependencies { @@ -98,8 +90,7 @@ object KeyguardDismissInteractorFactory { PowerInteractorFactory.create( repository = powerRepository, ) - val selectedUserInteractor = - SelectedUserInteractor(repository = userRepository, flags = featureFlags) + val selectedUserInteractor = SelectedUserInteractor(repository = userRepository) return WithDependencies( trustRepository = trustRepository, keyguardRepository = keyguardRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt new file mode 100644 index 000000000000..0357036d907c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt @@ -0,0 +1,24 @@ +/* + * 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.qs.tiles.impl.colorcorrection + +import com.android.systemui.accessibility.qs.QSAccessibilityModule +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.qsEventLogger + +val Kosmos.qsColorCorrectionTileConfig by + Kosmos.Fixture { QSAccessibilityModule.provideColorCorrectionTileConfig(qsEventLogger) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt index 8f972f52729f..18b3f47e3df7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt @@ -16,17 +16,20 @@ package com.android.systemui.qs.tiles.impl.custom.data.repository +import android.os.UserHandle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map class FakeCustomTilePackageUpdatesRepository : CustomTilePackageUpdatesRepository { - private val mutablePackageChanges = MutableSharedFlow<Unit>() + private val mutablePackageChangesForUser = MutableSharedFlow<UserHandle>() - override val packageChanges: Flow<Unit> - get() = mutablePackageChanges + override fun getPackageChangesForUser(user: UserHandle): Flow<Unit> = + mutablePackageChangesForUser.filter { it == user }.map {} - suspend fun emitPackageChange() { - mutablePackageChanges.emit(Unit) + suspend fun emitPackageChange(user: UserHandle) { + mutablePackageChangesForUser.emit(user) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index d78bcb93b256..0b41926ed13e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -387,7 +387,7 @@ class SceneTestUtils( } fun selectedUserInteractor(): SelectedUserInteractor { - return SelectedUserInteractor(userRepository, featureFlags) + return SelectedUserInteractor(userRepository) } fun bouncerActionButtonInteractor( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt index 427f92a7f514..89672f109657 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt @@ -16,9 +16,7 @@ package com.android.systemui.user.domain.interactor -import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.kosmos.Kosmos import com.android.systemui.user.data.repository.userRepository -val Kosmos.selectedUserInteractor by - Kosmos.Fixture { SelectedUserInteractor(userRepository, featureFlagsClassic) } +val Kosmos.selectedUserInteractor by Kosmos.Fixture { SelectedUserInteractor(userRepository) } diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp index b8cf13b11534..e2488a51bba1 100644 --- a/services/accessibility/Android.bp +++ b/services/accessibility/Android.bp @@ -19,6 +19,9 @@ java_library_static { defaults: [ "platform_service_defaults", ], + lint: { + error_checks: ["MissingPermissionAnnotation"], + }, srcs: [ ":services.accessibility-sources", "//frameworks/base/packages/SettingsLib/RestrictedLockUtils:SettingsLibRestrictedLockUtilsSrc", diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 97d36d443620..1d73843260cb 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -118,6 +118,7 @@ import java.util.Set; * This class represents an accessibility client - either an AccessibilityService or a UiAutomation. * It is responsible for behavior common to both types of clients. */ +@SuppressWarnings("MissingPermissionAnnotation") abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServiceConnection.Stub implements ServiceConnection, IBinder.DeathRecipient, KeyEventDispatcher.KeyEventFilter, FingerprintGestureDispatcher.FingerprintGestureClient { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 9ddc35ae240b..abcd8e2a2d7e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -57,6 +57,7 @@ import java.util.StringJoiner; * * NOTE: This class has to be created and poked only from the main thread. */ +@SuppressWarnings("MissingPermissionAnnotation") class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation { private static final String TAG = AccessibilityInputFilter.class.getSimpleName(); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index edb41639514b..3d8d7b738233 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -195,6 +195,7 @@ import java.util.function.Predicate; * event dispatch for {@link AccessibilityEvent}s generated across all processes * on the device. Events are dispatched to {@link AccessibilityService}s. */ +@SuppressWarnings("MissingPermissionAnnotation") public class AccessibilityManagerService extends IAccessibilityManager.Stub implements AbstractAccessibilityServiceConnection.SystemSupport, AccessibilityUserState.ServiceInfoChangeListener, diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 40ca694dddd8..5ebe16115a4b 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -65,6 +65,7 @@ import java.util.Set; * passed to the service it represents as soon it is bound. It also serves as the * connection for the service. */ +@SuppressWarnings("MissingPermissionAnnotation") class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection { private static final String LOG_TAG = "AccessibilityServiceConnection"; diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java index a525e7c64bc3..b119d7d117cd 100644 --- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java +++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java @@ -34,6 +34,7 @@ import java.util.List; * If we are stripping and/or replacing the actions from a window, we need to intercept the * nodes heading back to the service and swap out the actions. */ +@SuppressWarnings("MissingPermissionAnnotation") public class ActionReplacingCallback extends IAccessibilityInteractionConnectionCallback.Stub { private static final boolean DEBUG = false; private static final String LOG_TAG = "ActionReplacingCallback"; diff --git a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java index c9ec16edc54e..e10e87c51d59 100644 --- a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java +++ b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java @@ -33,6 +33,7 @@ import java.util.List; /** * Encapsulate fingerprint gesture logic */ +@SuppressWarnings("MissingPermissionAnnotation") public class FingerprintGestureDispatcher extends IFingerprintClientActiveCallback.Stub implements Handler.Callback{ private static final int MSG_REGISTER = 1; diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java index ab01fc324ed2..6aa4702ec7d5 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java @@ -64,6 +64,7 @@ import java.util.Set; * * TODO(241429275): Initialize this when a proxy is registered. */ +@SuppressWarnings("MissingPermissionAnnotation") public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection { private static final String LOG_TAG = "ProxyAccessibilityServiceConnection"; diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 53c629a9ed2d..f69104db7c10 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -245,6 +245,7 @@ class UiAutomationManager { } } + @SuppressWarnings("MissingPermissionAnnotation") private class UiAutomationService extends AbstractAccessibilityServiceConnection { private final Handler mMainHandler; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java index e6af54bd937c..e11c36a91830 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java @@ -922,6 +922,7 @@ public class MagnificationConnectionManager implements disableWindowMagnification(displayId, true); } + @SuppressWarnings("MissingPermissionAnnotation") private class ConnectionCallback extends IMagnificationConnectionCallback.Stub implements IBinder.DeathRecipient { private boolean mExpiredDeathRecipient = false; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java index c63784a79ffd..db5b3133169a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java @@ -246,6 +246,7 @@ class MagnificationConnectionWrapper { return new RemoteAnimationCallback(callback, trace); } + @SuppressWarnings("MissingPermissionAnnotation") private static class RemoteAnimationCallback extends IRemoteMagnificationAnimationCallback.Stub { private final MagnificationAnimationCallback mCallback; diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 77a5e3db2aba..a4b28967e3b2 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -72,6 +72,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.ShortcutServiceInternal; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; +import android.content.pm.UserPackage; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; @@ -559,10 +560,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId); } else if (provider.maskedBySuspendedPackage) { showBadge = mUserManager.hasBadge(appUserId); - final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage( + final UserPackage suspendingPackage = mPackageManagerInternal.getSuspendingPackage( appInfo.packageName, appUserId); // TODO(b/281839596): don't rely on platform always meaning suspended by admin. - if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { + if (suspendingPackage != null + && PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) { onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent( appUserId, true); } else { diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index ca6fefdd8245..b5130a1c4cfc 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -29,7 +29,7 @@ flag { } flag { - name: "ignore_invisible_view_group_in_assist_structure" + name: "include_invisible_view_group_in_assist_structure" namespace: "autofill" description: "Mitigation for autofill providers miscalculating view visibility" bug: "291795358" diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index 23e7ce68c1d0..9fdf5c2d0fc1 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -92,9 +92,10 @@ class CompanionDeviceShellCommand extends ShellCommand { int userId = getNextIntArgRequired(); String packageName = getNextArgRequired(); String address = getNextArgRequired(); + String deviceProfile = getNextArg(); final MacAddress macAddress = MacAddress.fromString(address); mService.createNewAssociation(userId, packageName, macAddress, - null, null, false); + null, deviceProfile, false); } break; @@ -350,7 +351,7 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" Print this help text."); pw.println(" list USER_ID"); pw.println(" List all Associations for a user."); - pw.println(" associate USER_ID PACKAGE MAC_ADDRESS"); + pw.println(" associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE]"); pw.println(" Create a new Association."); pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS"); pw.println(" Remove an existing Association."); diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 8dc6537d7e80..0d5cdcbe484c 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -439,6 +439,12 @@ public class VirtualDeviceManagerService extends SystemService { if (associationInfo == null) { throw new IllegalArgumentException("No association with ID " + associationId); } + if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES + .contains(associationInfo.getDeviceProfile()) + && Flags.persistentDeviceIdApi()) { + throw new IllegalArgumentException("Unsupported CDM Association device profile " + + associationInfo.getDeviceProfile() + " for virtual device creation."); + } Objects.requireNonNull(params); Objects.requireNonNull(activityListener); Objects.requireNonNull(soundEffectListener); diff --git a/services/core/Android.bp b/services/core/Android.bp index a0ccbf3acf0a..f5a80d80f271 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -200,6 +200,7 @@ java_library_static { "notification_flags_lib", "biometrics_flags_lib", "am_flags_lib", + "com_android_systemui_shared_flags_lib", "com_android_wm_shell_flags_lib", "com.android.server.utils_aconfig-java", "service-jobscheduler-deviceidle.flags-aconfig-java", diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 136692eb90d5..cac2efba1c89 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -289,11 +289,11 @@ public abstract class PackageManagerInternal { * * @param suspendedPackage The package that has been suspended. * @param userId The user for which to check. - * @return Name of the package that suspended the given package. Returns {@code null} if the - * given package is not currently suspended and the platform package name - i.e. - * {@code "android"} - if the package was suspended by a device admin. + * @return User id and package name of the package that suspended the given package. Returns + * {@code null} if the given package is not currently suspended and the platform package name + * - i.e. {@code "android"} - if the package was suspended by a device admin. */ - public abstract String getSuspendingPackage(String suspendedPackage, int userId); + public abstract UserPackage getSuspendingPackage(String suspendedPackage, int userId); /** * Suspend or unsuspend packages upon admin request. @@ -312,13 +312,13 @@ public abstract class PackageManagerInternal { * suspended application. * * @param suspendedPackage The package that has been suspended. - * @param suspendingPackage + * @param suspendingPackage The package responsible for suspension. * @param userId The user for which to check. * @return A {@link SuspendDialogInfo} object describing the dialog to be shown. */ @Nullable public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, - String suspendingPackage, int userId); + UserPackage suspendingPackage, int userId); /** * Gets any distraction flags set via @@ -1168,14 +1168,14 @@ public abstract class PackageManagerInternal { public abstract void clearBlockUninstallForUser(@UserIdInt int userId); /** - * Unsuspends all packages suspended by the given package for the user. + * Unsuspends all packages suspended by an admin for the user. */ - public abstract void unsuspendForSuspendingPackage(String suspendingPackage, int userId); + public abstract void unsuspendAdminSuspendedPackages(int userId); /** - * Returns {@code true} if the package is suspending any packages for the user. + * Returns {@code true} if an admin is suspending any packages for the user. */ - public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId); + public abstract boolean isAdminSuspendingAnyPackages(int userId); /** * Register to listen for loading progress of an installed package. diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 33726d17da62..5a44ac803cb4 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -974,6 +974,7 @@ public final class BatteryService extends SystemService { unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw); } break; case "get": { + final int opts = parseOptions(shell); final String key = shell.getNextArg(); if (key == null) { pw.println("No property specified"); @@ -1007,11 +1008,17 @@ public final class BatteryService extends SystemService { break; case "current_now": if (batteryServiceSupportCurrentAdbCommand()) { + if ((opts & OPTION_FORCE_UPDATE) != 0) { + updateHealthInfo(); + } pw.println(mHealthInfo.batteryCurrentMicroamps); } break; case "current_average": if (batteryServiceSupportCurrentAdbCommand()) { + if ((opts & OPTION_FORCE_UPDATE) != 0) { + updateHealthInfo(); + } pw.println(mHealthInfo.batteryCurrentAverageMicroamps); } break; @@ -1125,6 +1132,14 @@ public final class BatteryService extends SystemService { return 0; } + private void updateHealthInfo() { + try { + mHealthServiceWrapper.scheduleUpdate(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to update health service data.", e); + } + } + private void setChargerAcOnline(boolean online, boolean forceUpdate) { if (!mUpdatesStopped) { copyV1Battery(mLastHealthInfo, mHealthInfo); diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java index c7a560b52a01..9f31f375dafe 100644 --- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java +++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java @@ -226,20 +226,28 @@ public class AmbientContextManagerService extends List<AmbientContextManagerPerUserService> serviceList = new ArrayList<>(serviceNames.length); - if (serviceNames.length == 2) { + if (serviceNames.length == 2 + && !isDefaultService(serviceNames[0]) + && !isDefaultWearableService(serviceNames[1])) { Slog.i(TAG, "Not using default services, " + "services provided for testing should be exactly two services."); - if (!isDefaultService(serviceNames[0]) && !isDefaultWearableService(serviceNames[1])) { - serviceList.add(new DefaultAmbientContextManagerPerUserService( - this, mLock, resolvedUserId, - AmbientContextManagerPerUserService.ServiceType.DEFAULT, serviceNames[0])); - serviceList.add(new WearableAmbientContextManagerPerUserService( - this, mLock, resolvedUserId, - AmbientContextManagerPerUserService.ServiceType.WEARABLE, - serviceNames[1])); - } + serviceList.add( + new DefaultAmbientContextManagerPerUserService( + this, + mLock, + resolvedUserId, + AmbientContextManagerPerUserService.ServiceType.DEFAULT, + serviceNames[0])); + serviceList.add( + new WearableAmbientContextManagerPerUserService( + this, + mLock, + resolvedUserId, + AmbientContextManagerPerUserService.ServiceType.WEARABLE, + serviceNames[1])); return serviceList; - } else { + } + if (serviceNames.length > 2) { Slog.i(TAG, "Incorrect number of services provided for testing."); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 34cfdfaa7974..d138f24e7dc7 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -896,7 +896,8 @@ public class AudioDeviceInventory { if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { boolean codecChange = false; if (btInfo.mProfile == BluetoothProfile.A2DP - || btInfo.mProfile == BluetoothProfile.LE_AUDIO) { + || btInfo.mProfile == BluetoothProfile.LE_AUDIO + || btInfo.mProfile == BluetoothProfile.LE_AUDIO_BROADCAST) { if (di.mDeviceCodecFormat != codec) { di.mDeviceCodecFormat = codec; mConnectedDevices.replace(key, di); diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 401dc88669ec..8075618be200 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -324,7 +324,8 @@ public class BtHelper { // AUDIO_FORMAT_DEFAULT as native audio policy manager expects a specific audio format // only if audio HW module selection based on format is supported for the device type. if (!(profile == BluetoothProfile.A2DP - || (profile == BluetoothProfile.LE_AUDIO && isLeOutput))) { + || (isLeOutput && ((profile == BluetoothProfile.LE_AUDIO) + || (profile == BluetoothProfile.LE_AUDIO_BROADCAST))))) { return AudioSystem.AUDIO_FORMAT_DEFAULT; } @AudioSystem.AudioFormatNativeEnumForBtCodec int codec = diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 0629e6373b6e..dafea9a199fd 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -36,6 +36,7 @@ import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; +import android.hardware.biometrics.AuthenticationStateListener; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.ComponentInfoInternal; @@ -385,6 +386,26 @@ public class AuthService extends SystemService { } @Override + public void registerAuthenticationStateListener(AuthenticationStateListener listener) + throws RemoteException { + checkInternalPermission(); + final IFingerprintService fingerprintService = mInjector.getFingerprintService(); + if (fingerprintService != null) { + fingerprintService.registerAuthenticationStateListener(listener); + } + } + + @Override + public void unregisterAuthenticationStateListener(AuthenticationStateListener listener) + throws RemoteException { + checkInternalPermission(); + final IFingerprintService fingerprintService = mInjector.getFingerprintService(); + if (fingerprintService != null) { + fingerprintService.unregisterAuthenticationStateListener(listener); + } + } + + @Override public void invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback) throws RemoteException { checkInternalPermission(); diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index a47135fd032f..f9568ea9d72b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -27,7 +27,7 @@ import android.hardware.biometrics.AuthenticateOptions; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager; -import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.BiometricRequestConstants; import android.os.IBinder; import android.os.RemoteException; import android.security.KeyStore; @@ -430,19 +430,19 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions> return mLockoutTracker; } - protected int getShowOverlayReason() { + protected int getRequestReason() { if (isKeyguard()) { - return BiometricOverlayConstants.REASON_AUTH_KEYGUARD; + return BiometricRequestConstants.REASON_AUTH_KEYGUARD; } else if (isBiometricPrompt()) { // BP reason always takes precedent over settings, since callers from within // settings can always invoke BP. - return BiometricOverlayConstants.REASON_AUTH_BP; + return BiometricRequestConstants.REASON_AUTH_BP; } else if (isSettings()) { // This is pretty much only for FingerprintManager#authenticate usage from // FingerprintSettings. - return BiometricOverlayConstants.REASON_AUTH_SETTINGS; + return BiometricRequestConstants.REASON_AUTH_SETTINGS; } else { - return BiometricOverlayConstants.REASON_AUTH_OTHER; + return BiometricRequestConstants.REASON_AUTH_OTHER; } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java new file mode 100644 index 000000000000..58635353c780 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationStateListeners.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.annotation.NonNull; +import android.hardware.biometrics.AuthenticationStateListener; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Low-level callback interface between BiometricManager and AuthService. Allows core system + * services (e.g. SystemUI) to register and unregister listeners for updates about the current + * state of biometric authentication. + * @hide */ +public class AuthenticationStateListeners implements IBinder.DeathRecipient { + + private static final String TAG = "AuthenticationStateListeners"; + + @NonNull + private final CopyOnWriteArrayList<AuthenticationStateListener> mAuthenticationStateListeners = + new CopyOnWriteArrayList<>(); + + /** + * Enables clients to register an AuthenticationStateListener for updates about the current + * state of biometric authentication. + * @param listener listener to register + */ + public void registerAuthenticationStateListener( + @NonNull AuthenticationStateListener listener) { + mAuthenticationStateListeners.add(listener); + try { + listener.asBinder().linkToDeath(this, 0 /* flags */); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to link to death", e); + } + } + + /** + * Enables clients to unregister an AuthenticationStateListener. + * @param listener listener to register + */ + public void unregisterAuthenticationStateListener( + @NonNull AuthenticationStateListener listener) { + mAuthenticationStateListeners.remove(listener); + } + + /** + * Defines behavior in response to authentication starting + * @param requestReason reason from [BiometricRequestConstants.RequestReason] for requesting + * authentication starting + */ + public void onAuthenticationStarted(int requestReason) { + for (AuthenticationStateListener listener: mAuthenticationStateListeners) { + try { + listener.onAuthenticationStarted(requestReason); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in notifying listener that authentication " + + "started", e); + } + } + } + + /** + * Defines behavior in response to authentication stopping + */ + public void onAuthenticationStopped() { + for (AuthenticationStateListener listener: mAuthenticationStateListeners) { + try { + listener.onAuthenticationStopped(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in notifying listener that authentication " + + "stopped", e); + } + } + } + + @Override + public void binderDied() { + // Do nothing, handled below + } + + @Override + public void binderDied(IBinder who) { + Slog.w(TAG, "Callback binder died: " + who); + if (mAuthenticationStateListeners.removeIf(listener -> listener.asBinder().equals(who))) { + Slog.w(TAG, "Removed dead listener for " + who); + } else { + Slog.w(TAG, "No dead listeners found"); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index 483ce75eae98..2c4d30b622ad 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -19,7 +19,7 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.fingerprint.FingerprintManager; import android.os.IBinder; import android.os.RemoteException; @@ -135,14 +135,14 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En return true; } - protected int getOverlayReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) { + protected int getRequestReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) { switch (reason) { case FingerprintManager.ENROLL_FIND_SENSOR: - return BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR; + return BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR; case FingerprintManager.ENROLL_ENROLL: - return BiometricOverlayConstants.REASON_ENROLL_ENROLLING; + return BiometricRequestConstants.REASON_ENROLL_ENROLLING; default: - return BiometricOverlayConstants.REASON_UNKNOWN; + return BiometricRequestConstants.REASON_UNKNOWN; } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java index aeb6b6e2a907..3d20efc88c77 100644 --- a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java +++ b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java @@ -16,9 +16,11 @@ package com.android.server.biometrics.sensors; +import static com.android.systemui.shared.Flags.sidefpsControllerRefactor; + import android.annotation.NonNull; import android.annotation.Nullable; -import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; @@ -42,6 +44,8 @@ public final class SensorOverlays { private static final String TAG = "SensorOverlays"; @NonNull private final Optional<IUdfpsOverlayController> mUdfpsOverlayController; + + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @NonNull private final Optional<ISidefpsController> mSidefpsController; /** @@ -58,19 +62,32 @@ public final class SensorOverlays { } /** + * Create an overlay controller for each modality. + * + * @param udfpsOverlayController under display fps or null if not present on device + */ + public SensorOverlays(@Nullable IUdfpsOverlayController udfpsOverlayController) { + mUdfpsOverlayController = Optional.ofNullable(udfpsOverlayController); + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR + mSidefpsController = Optional.empty(); + } + + /** * Show the overlay. * * @param sensorId sensor id * @param reason reason for showing * @param client client performing operation */ - public void show(int sensorId, @BiometricOverlayConstants.ShowReason int reason, + public void show(int sensorId, @BiometricRequestConstants.RequestReason int reason, @NonNull AcquisitionClient<?> client) { - if (mSidefpsController.isPresent()) { - try { - mSidefpsController.get().show(sensorId, reason); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when showing the side-fps overlay", e); + if (!sidefpsControllerRefactor()) { + if (mSidefpsController.isPresent()) { + try { + mSidefpsController.get().show(sensorId, reason); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when showing the side-fps overlay", e); + } } } @@ -98,11 +115,13 @@ public final class SensorOverlays { * @param sensorId sensor id */ public void hide(int sensorId) { - if (mSidefpsController.isPresent()) { - try { - mSidefpsController.get().hide(sensorId); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e); + if (!sidefpsControllerRefactor()) { + if (mSidefpsController.isPresent()) { + try { + mSidefpsController.get().hide(sensorId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e); + } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 769554315b6e..83b306b07c27 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -33,6 +33,7 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; +import android.hardware.biometrics.AuthenticationStateListener; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -81,6 +82,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.SystemService; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -128,6 +130,8 @@ public class FingerprintService extends SystemService { private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal> mBiometricStateCallback; @NonNull + private final AuthenticationStateListeners mAuthenticationStateListeners; + @NonNull private final Handler mHandler; @NonNull private final FingerprintServiceRegistry mRegistry; @@ -891,6 +895,7 @@ public class FingerprintService extends SystemService { return providers; }); } + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void addAuthenticatorsRegisteredCallback( @@ -902,6 +907,24 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override + public void registerAuthenticationStateListener( + @NonNull AuthenticationStateListener listener) { + super.registerAuthenticationStateListener_enforcePermission(); + + mAuthenticationStateListeners.registerAuthenticationStateListener(listener); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) + @Override + public void unregisterAuthenticationStateListener( + @NonNull AuthenticationStateListener listener) { + super.unregisterAuthenticationStateListener_enforcePermission(); + + mAuthenticationStateListeners.unregisterAuthenticationStateListener(listener); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) + @Override public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { super.registerBiometricStateListener_enforcePermission(); @@ -957,6 +980,7 @@ public class FingerprintService extends SystemService { } } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void setSidefpsController(@NonNull ISidefpsController controller) { @@ -1014,6 +1038,7 @@ public class FingerprintService extends SystemService { mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context)); + mAuthenticationStateListeners = new AuthenticationStateListeners(); mFingerprintProvider = fingerprintProvider != null ? fingerprintProvider : (name) -> { final String fqName = IFingerprint.DESCRIPTOR + "/" + name; @@ -1022,9 +1047,9 @@ public class FingerprintService extends SystemService { if (fp != null) { try { return new FingerprintProvider(getContext(), - mBiometricStateCallback, fp.getSensorProps(), name, - mLockoutResetDispatcher, mGestureAvailabilityDispatcher, - mBiometricContext); + mBiometricStateCallback, mAuthenticationStateListeners, + fp.getSensorProps(), name, mLockoutResetDispatcher, + mGestureAvailabilityDispatcher, mBiometricContext); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); } @@ -1086,13 +1111,13 @@ public class FingerprintService extends SystemService { Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */, UserHandle.USER_CURRENT) != 0) { fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), - mBiometricStateCallback, hidlSensor, - mLockoutResetDispatcher, mGestureAvailabilityDispatcher, + mBiometricStateCallback, mAuthenticationStateListeners, + hidlSensor, mLockoutResetDispatcher, mGestureAvailabilityDispatcher, BiometricContext.getInstance(getContext())); } else { fingerprint21 = Fingerprint21.newInstance(getContext(), - mBiometricStateCallback, hidlSensor, mHandler, - mLockoutResetDispatcher, mGestureAvailabilityDispatcher); + mBiometricStateCallback, mAuthenticationStateListeners, hidlSensor, + mHandler, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } providers.add(fingerprint21); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index a15d1a4df39f..fc37d7020a21 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -135,6 +135,7 @@ public interface ServiceProvider extends void onPowerPressed(); + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR /** * Sets side-fps controller * @param controller side-fps controller diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 337c3c299e54..29c5a3de3a80 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static com.android.systemui.shared.Flags.sidefpsControllerRefactor; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskStackListener; @@ -48,6 +50,7 @@ import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -84,6 +87,7 @@ public class FingerprintAuthenticationClient private final int mSkipWaitForPowerVendorAcquireMessage; private final long mFingerUpIgnoresPower = 500; private final AuthSessionCoordinator mAuthSessionCoordinator; + @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners; @Nullable private ICancellationSignal mCancellationSignal; private boolean mIsPointerDown; @@ -110,7 +114,9 @@ public class FingerprintAuthenticationClient boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener, @Nullable IUdfpsOverlayController udfpsOverlayController, + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Nullable ISidefpsController sidefpsController, + @NonNull AuthenticationStateListeners authenticationStateListeners, boolean allowBackgroundAuthentication, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull Handler handler, @@ -136,7 +142,12 @@ public class FingerprintAuthenticationClient false /* shouldVibrate */, biometricStrength); setRequestId(requestId); - mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); + if (sidefpsControllerRefactor()) { + mSensorOverlays = new SensorOverlays(udfpsOverlayController); + } else { + mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); + } + mAuthenticationStateListeners = authenticationStateListeners; mSensorProps = sensorProps; mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */); mHandler = handler; @@ -216,6 +227,9 @@ public class FingerprintAuthenticationClient if (authenticated) { mState = STATE_STOPPED; mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } } else { mState = STATE_STARTED_PAUSED_ATTEMPTED; } @@ -241,6 +255,9 @@ public class FingerprintAuthenticationClient // controlled by the HAL, the framework must stop the sensor before finishing the // client. mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */); cancel(); } @@ -266,11 +283,17 @@ public class FingerprintAuthenticationClient } mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } } @Override protected void startHalOperation() { - mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this); + mSensorOverlays.show(getSensorId(), getRequestReason(), this); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason()); + } try { mCancellationSignal = doAuthenticate(); @@ -280,6 +303,9 @@ public class FingerprintAuthenticationClient BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } mCallback.onClientFinished(this, false /* success */); } } @@ -323,6 +349,9 @@ public class FingerprintAuthenticationClient @Override protected void stopHalOperation() { mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } unsubscribeBiometricContext(); if (mCancellationSignal != null) { @@ -423,6 +452,9 @@ public class FingerprintAuthenticationClient } mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } mCallback.onClientFinished(this, false /* success */); } @@ -450,6 +482,9 @@ public class FingerprintAuthenticationClient } mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } mCallback.onClientFinished(this, false /* success */); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index e2413ee1c016..e58e5ae117b5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -16,10 +16,12 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static com.android.systemui.shared.Flags.sidefpsControllerRefactor; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -66,7 +68,12 @@ public class FingerprintDetectClient extends AcquisitionClient<AidlSession> true /* shouldVibrate */, biometricLogger, biometricContext); setRequestId(requestId); mIsStrongBiometric = isStrongBiometric; - mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/); + if (sidefpsControllerRefactor()) { + mSensorOverlays = new SensorOverlays(udfpsOverlayController); + } else { + mSensorOverlays = new SensorOverlays( + udfpsOverlayController, null /* sideFpsController */); + } mOptions = options; } @@ -93,7 +100,7 @@ public class FingerprintDetectClient extends AcquisitionClient<AidlSession> @Override protected void startHalOperation() { - mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD, + mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD, this); try { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 06550d8b4fce..c0761ed8f32b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static com.android.systemui.shared.Flags.sidefpsControllerRefactor; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -42,6 +44,7 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.log.Probe; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -65,6 +68,7 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback; private final @FingerprintManager.EnrollReason int mEnrollReason; + @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners; @Nullable private ICancellationSignal mCancellationSignal; private final int mMaxTemplatesPerUser; private boolean mIsPointerDown; @@ -80,15 +84,18 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement } } - public FingerprintEnrollClient(@NonNull Context context, - @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, long requestId, + public FingerprintEnrollClient( + @NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, + @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull FingerprintSensorPropertiesInternal sensorProps, @Nullable IUdfpsOverlayController udfpsOverlayController, + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Nullable ISidefpsController sidefpsController, + @NonNull AuthenticationStateListeners authenticationStateListeners, int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) { // UDFPS haptics occur when an image is acquired (instead of when the result is known) super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, @@ -96,7 +103,13 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement logger, biometricContext); setRequestId(requestId); mSensorProps = sensorProps; - mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); + if (sidefpsControllerRefactor()) { + mSensorOverlays = new SensorOverlays(udfpsOverlayController); + } else { + mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); + } + mAuthenticationStateListeners = authenticationStateListeners; + mMaxTemplatesPerUser = maxTemplatesPerUser; mALSProbeCallback = getLogger().getAmbientLightProbe(true /* startWithClient */); @@ -130,7 +143,11 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement if (remaining == 0) { mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } } + } @Override @@ -159,8 +176,10 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement @Override public void onError(int errorCode, int vendorCode) { super.onError(errorCode, vendorCode); - mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } } @Override @@ -171,8 +190,12 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement @Override protected void startHalOperation() { - mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), + mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason), this); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStarted( + getRequestReasonFromEnrollReason(mEnrollReason)); + } BiometricNotificationUtils.cancelBadCalibrationNotification(getContext()); try { @@ -210,6 +233,10 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement @Override protected void stopHalOperation() { mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } + unsubscribeBiometricContext(); if (mCancellationSignal != null) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 9985b06833c9..032ab87196f8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -64,6 +64,7 @@ import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; @@ -108,6 +109,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final BiometricStateCallback mBiometricStateCallback; @NonNull + private final AuthenticationStateListeners mAuthenticationStateListeners; + @NonNull private final String mHalInstanceName; @NonNull private final Handler mHandler; @@ -122,6 +125,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final BiometricContext mBiometricContext; @Nullable private IFingerprint mDaemon; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Nullable private ISidefpsController mSidefpsController; private AuthSessionCoordinator mAuthSessionCoordinator; @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; @@ -157,16 +161,19 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public FingerprintProvider(@NonNull Context context, @NonNull BiometricStateCallback biometricStateCallback, + @NonNull AuthenticationStateListeners authenticationStateListeners, @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext) { - this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher, - gestureAvailabilityDispatcher, biometricContext, null /* daemon */); + this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName, + lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, + null /* daemon */); } @VisibleForTesting FingerprintProvider(@NonNull Context context, @NonNull BiometricStateCallback biometricStateCallback, + @NonNull AuthenticationStateListeners authenticationStateListeners, @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @@ -174,6 +181,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi IFingerprint daemon) { mContext = context; mBiometricStateCallback = biometricStateCallback; + mAuthenticationStateListeners = authenticationStateListeners; mHalInstanceName = halInstanceName; mFingerprintSensors = new SensorList<>(ActivityManager.getService()); mHandler = new Handler(Looper.getMainLooper()); @@ -434,7 +442,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mBiometricContext, mFingerprintSensors.get(sensorId).getSensorProperties(), mUdfpsOverlayController, mSidefpsController, - maxTemplatesPerUser, enrollReason); + mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason); scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback( mBiometricStateCallback, new ClientMonitorCallback() { @Override @@ -498,7 +506,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mBiometricContext, isStrongBiometric, mTaskStackListener, mUdfpsOverlayController, mSidefpsController, - allowBackgroundAuthentication, + mAuthenticationStateListeners, allowBackgroundAuthentication, mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler, Utils.getCurrentStrength(sensorId), SystemClock.elapsedRealtimeClock(), @@ -732,6 +740,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Override public void setSidefpsController(@NonNull ISidefpsController controller) { mSidefpsController = controller; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 8bfa560b8031..d3cecd0e34c7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -68,6 +68,7 @@ import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; @@ -115,6 +116,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final Context mContext; @NonNull private final BiometricStateCallback mBiometricStateCallback; + @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners; private final ActivityTaskManager mActivityTaskManager; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; private final BiometricScheduler mScheduler; @@ -128,6 +130,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Nullable private IBiometricsFingerprint mDaemon; @NonNull private final HalResultController mHalResultController; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; + + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Nullable private ISidefpsController mSidefpsController; @NonNull private final BiometricContext mBiometricContext; @Nullable private AuthenticationStatsCollector mAuthenticationStatsCollector; @@ -330,6 +334,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @VisibleForTesting Fingerprint21(@NonNull Context context, @NonNull BiometricStateCallback biometricStateCallback, + @NonNull AuthenticationStateListeners authenticationStateListeners, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull BiometricScheduler scheduler, @NonNull Handler handler, @@ -338,6 +343,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull BiometricContext biometricContext) { mContext = context; mBiometricStateCallback = biometricStateCallback; + mAuthenticationStateListeners = authenticationStateListeners; mBiometricContext = biometricContext; mSensorProperties = sensorProps; @@ -378,6 +384,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public static Fingerprint21 newInstance(@NonNull Context context, @NonNull BiometricStateCallback biometricStateCallback, + @NonNull AuthenticationStateListeners authenticationStateListeners, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @@ -388,8 +395,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider gestureAvailabilityDispatcher); final HalResultController controller = new HalResultController(sensorProps.sensorId, context, handler, scheduler); - return new Fingerprint21(context, biometricStateCallback, sensorProps, scheduler, handler, - lockoutResetDispatcher, controller, BiometricContext.getInstance(context)); + return new Fingerprint21(context, biometricStateCallback, authenticationStateListeners, + sensorProps, scheduler, handler, lockoutResetDispatcher, controller, + BiometricContext.getInstance(context)); } @Override @@ -719,7 +727,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), - mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason); + mBiometricContext, mUdfpsOverlayController, + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR + mSidefpsController, + mAuthenticationStateListeners, enrollReason); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { @@ -761,10 +772,14 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), mBiometricContext, null /* sensorProps */, - mUdfpsOverlayController, mSidefpsController, + mUdfpsOverlayController, + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR + mSidefpsController, + mAuthenticationStateListeners, mContext.getResources().getInteger( com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser), enrollReason); + mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { @@ -880,6 +895,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mBiometricContext, isStrongBiometric, mTaskStackListener, mUdfpsOverlayController, mSidefpsController, + mAuthenticationStateListeners, allowBackgroundAuthentication, mSensorProperties, mHandler, Utils.getCurrentStrength(mSensorId), null /* clock */, mLockoutTracker); mScheduler.scheduleClientMonitor(client, mBiometricStateCallback); @@ -898,6 +914,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mBiometricContext, isStrongBiometric, mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, mSidefpsController, + mAuthenticationStateListeners, allowBackgroundAuthentication, mSensorProperties, Utils.getCurrentStrength(mSensorId)); mScheduler.scheduleClientMonitor(client, mBiometricStateCallback); @@ -1123,6 +1140,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mUdfpsOverlayController = controller; } + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Override public void setSidefpsController(@NonNull ISidefpsController controller) { mSidefpsController = controller; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index c1a9370b5423..88dae6fcc453 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -41,6 +41,7 @@ import android.util.SparseBooleanArray; import com.android.internal.R; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.AuthenticationConsumer; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; @@ -248,6 +249,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage public static Fingerprint21UdfpsMock newInstance(@NonNull Context context, @NonNull BiometricStateCallback biometricStateCallback, + @NonNull AuthenticationStateListeners authenticationStateListeners, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @@ -259,8 +261,9 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher); final MockHalResultController controller = new MockHalResultController(sensorProps.sensorId, context, handler, scheduler); - return new Fingerprint21UdfpsMock(context, biometricStateCallback, sensorProps, scheduler, - handler, lockoutResetDispatcher, controller, biometricContext); + return new Fingerprint21UdfpsMock(context, biometricStateCallback, + authenticationStateListeners, sensorProps, scheduler, handler, + lockoutResetDispatcher, controller, biometricContext); } private static abstract class FakeFingerRunnable implements Runnable { @@ -388,14 +391,15 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage private Fingerprint21UdfpsMock(@NonNull Context context, @NonNull BiometricStateCallback biometricStateCallback, + @NonNull AuthenticationStateListeners authenticationStateListeners, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull TestableBiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull MockHalResultController controller, @NonNull BiometricContext biometricContext) { - super(context, biometricStateCallback, sensorProps, scheduler, handler, - lockoutResetDispatcher, controller, biometricContext); + super(context, biometricStateCallback, authenticationStateListeners, sensorProps, scheduler, + handler, lockoutResetDispatcher, controller, biometricContext); mScheduler = scheduler; mScheduler.init(this); mHandler = handler; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index 9966e910d502..4c1d4d6d6d12 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; +import static com.android.systemui.shared.Flags.sidefpsControllerRefactor; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskStackListener; @@ -40,6 +42,7 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -68,6 +71,7 @@ class FingerprintAuthenticationClient @NonNull private final SensorOverlays mSensorOverlays; @NonNull private final FingerprintSensorPropertiesInternal mSensorProps; @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback; + @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners; private boolean mIsPointerDown; @@ -81,7 +85,9 @@ class FingerprintAuthenticationClient @NonNull TaskStackListener taskStackListener, @NonNull LockoutFrameworkImpl lockoutTracker, @Nullable IUdfpsOverlayController udfpsOverlayController, + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Nullable ISidefpsController sidefpsController, + @NonNull AuthenticationStateListeners authenticationStateListeners, boolean allowBackgroundAuthentication, @NonNull FingerprintSensorPropertiesInternal sensorProps, @Authenticators.Types int sensorStrength) { @@ -91,7 +97,12 @@ class FingerprintAuthenticationClient false /* shouldVibrate */, sensorStrength); setRequestId(requestId); mLockoutFrameworkImpl = lockoutTracker; - mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); + if (sidefpsControllerRefactor()) { + mSensorOverlays = new SensorOverlays(udfpsOverlayController); + } else { + mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); + } + mAuthenticationStateListeners = authenticationStateListeners; mSensorProps = sensorProps; mALSProbeCallback = getLogger().getAmbientLightProbe(false /* startWithClient */); } @@ -128,6 +139,9 @@ class FingerprintAuthenticationClient mState = STATE_STOPPED; resetFailedAttempts(getTargetUserId()); mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } } else { mState = STATE_STARTED_PAUSED_ATTEMPTED; final @LockoutTracker.LockoutMode int lockoutMode = @@ -141,6 +155,9 @@ class FingerprintAuthenticationClient // controlled by the HAL, the framework must stop the sensor before finishing the // client. mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */); cancel(); } @@ -156,6 +173,9 @@ class FingerprintAuthenticationClient } mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } } private void resetFailedAttempts(int userId) { @@ -205,7 +225,10 @@ class FingerprintAuthenticationClient @Override protected void startHalOperation() { - mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this); + mSensorOverlays.show(getSensorId(), getRequestReason(), this); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason()); + } try { // GroupId was never used. In fact, groupId is always the same as userId. @@ -215,6 +238,9 @@ class FingerprintAuthenticationClient onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } mCallback.onClientFinished(this, false /* success */); } } @@ -222,6 +248,9 @@ class FingerprintAuthenticationClient @Override protected void stopHalOperation() { mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } try { getFreshDaemon().cancel(); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index 0d7f9f23e0e1..6e029d2268e2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java @@ -16,12 +16,14 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; +import static com.android.systemui.shared.Flags.sidefpsControllerRefactor; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; -import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.FingerprintAuthenticateOptions; @@ -71,7 +73,12 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> options.getOpPackageName(), 0 /* cookie */, options.getSensorId(), true /* shouldVibrate */, biometricLogger, biometricContext); setRequestId(requestId); - mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */); + if (sidefpsControllerRefactor()) { + mSensorOverlays = new SensorOverlays(udfpsOverlayController); + } else { + mSensorOverlays = new SensorOverlays( + udfpsOverlayController, null /* sideFpsController */); + } mIsStrongBiometric = isStrongBiometric; } @@ -97,7 +104,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> @Override protected void startHalOperation() { - mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD, + mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD, this); try { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index 382e7e2121f4..26332ff6893c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; +import static com.android.systemui.shared.Flags.sidefpsControllerRefactor; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -34,6 +36,7 @@ import android.util.Slog; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -58,22 +61,31 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @NonNull private final SensorOverlays mSensorOverlays; private final @FingerprintManager.EnrollReason int mEnrollReason; + @NonNull private final AuthenticationStateListeners mAuthenticationStateListeners; private boolean mIsPointerDown; - FingerprintEnrollClient(@NonNull Context context, - @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, - long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId, + FingerprintEnrollClient( + @NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, + @NonNull IBinder token, long requestId, + @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId, @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, @Nullable IUdfpsOverlayController udfpsOverlayController, + // TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR @Nullable ISidefpsController sidefpsController, + @NonNull AuthenticationStateListeners authenticationStateListeners, @FingerprintManager.EnrollReason int enrollReason) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger, biometricContext); setRequestId(requestId); - mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); + if (sidefpsControllerRefactor()) { + mSensorOverlays = new SensorOverlays(udfpsOverlayController); + } else { + mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); + } + mAuthenticationStateListeners = authenticationStateListeners; mEnrollReason = enrollReason; if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) { @@ -110,8 +122,12 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @Override protected void startHalOperation() { - mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), + mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason), this); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStarted( + getRequestReasonFromEnrollReason(mEnrollReason)); + } BiometricNotificationUtils.cancelBadCalibrationNotification(getContext()); try { @@ -122,6 +138,9 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } mCallback.onClientFinished(this, false /* success */); } } @@ -129,6 +148,9 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @Override protected void stopHalOperation() { mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } try { getFreshDaemon().cancel(); @@ -149,6 +171,9 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint if (remaining == 0) { mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } } } @@ -170,6 +195,9 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint super.onError(errorCode, vendorCode); mSensorOverlays.hide(getSensorId()); + if (sidefpsControllerRefactor()) { + mAuthenticationStateListeners.onAuthenticationStopped(); + } } @Override diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index dff14b5fbdd0..6ec6a123a4e7 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -327,7 +327,7 @@ public final class DeviceStateManagerService extends SystemService { Optional<DeviceState> getOverrideState() { synchronized (mLock) { if (mActiveOverride.isPresent()) { - return getStateLocked(mActiveOverride.get().getRequestedState()); + return getStateLocked(mActiveOverride.get().getRequestedStateIdentifier()); } return Optional.empty(); } @@ -342,7 +342,7 @@ public final class DeviceStateManagerService extends SystemService { Optional<DeviceState> getOverrideBaseState() { synchronized (mLock) { if (mActiveBaseStateOverride.isPresent()) { - return getStateLocked(mActiveBaseStateOverride.get().getRequestedState()); + return getStateLocked(mActiveBaseStateOverride.get().getRequestedStateIdentifier()); } return Optional.empty(); } @@ -499,6 +499,7 @@ public final class DeviceStateManagerService extends SystemService { * @return {@code true} if the pending state has changed as a result of this call, {@code false} * otherwise. */ + @GuardedBy("mLock") private boolean updatePendingStateLocked() { if (mPendingState.isPresent()) { // Have pending state, can not configure a new state until the state is committed. @@ -507,7 +508,8 @@ public final class DeviceStateManagerService extends SystemService { final DeviceState stateToConfigure; if (mActiveOverride.isPresent()) { - stateToConfigure = getStateLocked(mActiveOverride.get().getRequestedState()).get(); + stateToConfigure = getStateLocked( + mActiveOverride.get().getRequestedStateIdentifier()).get(); } else if (mBaseState.isPresent() && isSupportedStateLocked(mBaseState.get().getIdentifier())) { // Base state could have recently become unsupported after a change in supported states. @@ -599,7 +601,7 @@ public final class DeviceStateManagerService extends SystemService { // requested state is committed. OverrideRequest activeRequest = mActiveOverride.orElse(null); if (activeRequest != null - && activeRequest.getRequestedState() == newState.getIdentifier()) { + && activeRequest.getRequestedStateIdentifier() == newState.getIdentifier()) { ProcessRecord processRecord = mProcessRecords.get(activeRequest.getPid()); if (processRecord != null) { processRecord.notifyRequestActiveAsync(activeRequest.getToken()); @@ -666,21 +668,21 @@ public final class DeviceStateManagerService extends SystemService { case STATUS_ACTIVE: mActiveOverride = Optional.of(request); mDeviceStateNotificationController.showStateActiveNotificationIfNeeded( - request.getRequestedState(), request.getUid()); + request.getRequestedStateIdentifier(), request.getUid()); break; case STATUS_CANCELED: if (mActiveOverride.isPresent() && mActiveOverride.get() == request) { mActiveOverride = Optional.empty(); mDeviceStateNotificationController.cancelNotification( - request.getRequestedState()); + request.getRequestedStateIdentifier()); if ((flags & FLAG_THERMAL_CRITICAL) == FLAG_THERMAL_CRITICAL) { mDeviceStateNotificationController .showThermalCriticalNotificationIfNeeded( - request.getRequestedState()); + request.getRequestedStateIdentifier()); } else if ((flags & FLAG_POWER_SAVE_ENABLED) == FLAG_POWER_SAVE_ENABLED) { mDeviceStateNotificationController .showPowerSaveNotificationIfNeeded( - request.getRequestedState()); + request.getRequestedStateIdentifier()); } } break; @@ -723,7 +725,7 @@ public final class DeviceStateManagerService extends SystemService { */ @GuardedBy("mLock") private void enableBaseStateRequestLocked(OverrideRequest request) { - setBaseState(request.getRequestedState()); + setBaseState(request.getRequestedStateIdentifier()); mActiveBaseStateOverride = Optional.of(request); ProcessRecord processRecord = mProcessRecords.get(request.getPid()); processRecord.notifyRequestActiveAsync(request.getToken()); @@ -762,6 +764,11 @@ public final class DeviceStateManagerService extends SystemService { synchronized (mLock) { mProcessRecords.remove(processRecord.mPid); mOverrideRequestController.handleProcessDied(processRecord.mPid); + + if (shouldCancelOverrideRequestWhenRequesterNotOnTop()) { + OverrideRequest request = mActiveOverride.get(); + mOverrideRequestController.cancelRequest(request); + } } } @@ -787,7 +794,7 @@ public final class DeviceStateManagerService extends SystemService { } OverrideRequest request = new OverrideRequest(token, callingPid, callingUid, - state, flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay if (!hasControlDeviceStatePermission && mRearDisplayState != null @@ -848,7 +855,7 @@ public final class DeviceStateManagerService extends SystemService { } OverrideRequest request = new OverrideRequest(token, callingPid, callingUid, - state, flags, OVERRIDE_REQUEST_TYPE_BASE_STATE); + deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_BASE_STATE); mOverrideRequestController.addBaseStateRequest(request); } } @@ -1318,7 +1325,7 @@ public final class DeviceStateManagerService extends SystemService { if (mActiveOverride.isEmpty()) { return false; } - int identifier = mActiveOverride.get().getRequestedState(); + int identifier = mActiveOverride.get().getRequestedStateIdentifier(); DeviceState deviceState = mDeviceStates.get(identifier); return deviceState.hasFlag(DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP); } diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java index 74cf184e826e..20485c1ac102 100644 --- a/services/core/java/com/android/server/devicestate/OverrideRequest.java +++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java @@ -17,6 +17,7 @@ package com.android.server.devicestate; import android.annotation.IntDef; +import android.annotation.NonNull; import android.hardware.devicestate.DeviceStateRequest; import android.os.IBinder; @@ -32,7 +33,8 @@ final class OverrideRequest { private final IBinder mToken; private final int mPid; private final int mUid; - private final int mRequestedState; + @NonNull + private final DeviceState mRequestedState; @DeviceStateRequest.RequestFlags private final int mFlags; @OverrideRequestType @@ -69,7 +71,7 @@ final class OverrideRequest { @Retention(RetentionPolicy.SOURCE) public @interface OverrideRequestType {} - OverrideRequest(IBinder token, int pid, int uid, int requestedState, + OverrideRequest(IBinder token, int pid, int uid, @NonNull DeviceState requestedState, @DeviceStateRequest.RequestFlags int flags, @OverrideRequestType int requestType) { mToken = token; mPid = pid; @@ -91,10 +93,15 @@ final class OverrideRequest { return mUid; } - int getRequestedState() { + @NonNull + DeviceState getRequestedDeviceState() { return mRequestedState; } + int getRequestedStateIdentifier() { + return mRequestedState.getIdentifier(); + } + @DeviceStateRequest.RequestFlags int getFlags() { return mFlags; diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java index 46f0bc0d9805..f5f2fa8cabdc 100644 --- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java +++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java @@ -204,6 +204,12 @@ final class OverrideRequestController { } if (mRequest != null && mRequest.getPid() == pid) { + if (mRequest.getRequestedDeviceState().hasFlag( + DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)) { + cancelCurrentRequestLocked(); + return; + } + if (mStickyRequestsAllowed) { // Do not cancel the requests now because sticky requests are allowed. These // requests will be cancelled on a call to cancelStickyRequests(). @@ -219,7 +225,7 @@ final class OverrideRequestController { * listener of all changes to request status as a result of this change. */ void handleBaseStateChanged(int state) { - if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) { + if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedStateIdentifier()) { cancelBaseStateOverrideRequest(); } if (mRequest == null) { @@ -246,11 +252,12 @@ final class OverrideRequestController { flags |= isThermalCritical ? FLAG_THERMAL_CRITICAL : 0; flags |= isPowerSaveEnabled ? FLAG_POWER_SAVE_ENABLED : 0; if (mBaseStateRequest != null && !contains(newSupportedStates, - mBaseStateRequest.getRequestedState())) { + mBaseStateRequest.getRequestedStateIdentifier())) { cancelCurrentBaseStateRequestLocked(flags); } - if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) { + if (mRequest != null && !contains(newSupportedStates, + mRequest.getRequestedStateIdentifier())) { cancelCurrentRequestLocked(flags); } } @@ -262,7 +269,7 @@ final class OverrideRequestController { pw.println("Override Request active: " + requestActive); if (requestActive) { pw.println("Request: mPid=" + overrideRequest.getPid() - + ", mRequestedState=" + overrideRequest.getRequestedState() + + ", mRequestedState=" + overrideRequest.getRequestedStateIdentifier() + ", mFlags=" + overrideRequest.getFlags() + ", mStatus=" + statusToString(STATUS_ACTIVE)); } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 80c3a270efdf..b54289321e89 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -583,8 +583,8 @@ public class AutomaticBrightnessController { pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate); pw.println(" mAmbientLux=" + mAmbientLux); pw.println(" mAmbientLuxValid=" + mAmbientLuxValid); - pw.println(" mPreThesholdLux=" + mPreThresholdLux); - pw.println(" mPreThesholdBrightness=" + mPreThresholdBrightness); + pw.println(" mPreThresholdLux=" + mPreThresholdLux); + pw.println(" mPreThresholdBrightness=" + mPreThresholdBrightness); pw.println(" mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold); pw.println(" mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold); pw.println(" mScreenBrighteningThreshold=" + mScreenBrighteningThreshold); diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index bd5e189a5404..aa7f07d24dbf 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -93,6 +93,10 @@ public class DisplayManagerFlags { Flags.FLAG_ENABLE_EXTERNAL_VSYNC_PROXIMITY_VOTE, Flags::enableExternalVsyncProximityVote); + private final FlagState mVsyncLowPowerVote = new FlagState( + Flags.FLAG_ENABLE_VSYNC_LOW_POWER_VOTE, + Flags::enableVsyncLowPowerVote); + private final FlagState mBrightnessWearBedtimeModeClamperFlagState = new FlagState( Flags.FLAG_BRIGHTNESS_WEAR_BEDTIME_MODE_CLAMPER, Flags::brightnessWearBedtimeModeClamper); @@ -125,7 +129,6 @@ public class DisplayManagerFlags { return mPowerThrottlingClamperFlagState.isEnabled(); } - /** * Returns whether adaptive tone improvements are enabled */ @@ -198,6 +201,10 @@ public class DisplayManagerFlags { return mVsyncProximityVote.isEnabled(); } + public boolean isVsyncLowPowerVoteEnabled() { + return mVsyncLowPowerVote.isEnabled(); + } + public boolean isBrightnessWearBedtimeModeClamperEnabled() { return mBrightnessWearBedtimeModeClamperFlagState.isEnabled(); } diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 7a723a3290a9..04ecbb9e08ad 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -130,6 +130,14 @@ flag { } flag { + name: "enable_vsync_low_power_vote" + namespace: "display_manager" + description: "Feature flag for vsync low power vote" + bug: "314920284" + is_fixed_read_only: true +} + +flag { name: "brightness_wear_bedtime_mode_clamper" namespace: "display_manager" description: "Feature flag for the Wear Bedtime mode brightness clamper" diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index f7b540bc3753..6e503cbd68f3 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -179,7 +179,7 @@ public class DisplayModeDirector { private final boolean mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled; - private final boolean mVsyncProximityVoteEnabled; + private final boolean mDvrrSupported; public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, @@ -190,6 +190,8 @@ public class DisplayModeDirector { public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, @NonNull Injector injector, @NonNull DisplayManagerFlags displayManagerFlags) { + mDvrrSupported = context.getResources().getBoolean( + com.android.internal.R.bool.config_supportsDvrr); mIsDisplayResolutionRangeVotingEnabled = displayManagerFlags .isDisplayResolutionRangeVotingEnabled(); mIsUserPreferredModeVoteEnabled = displayManagerFlags.isUserPreferredModeVoteEnabled(); @@ -199,7 +201,6 @@ public class DisplayModeDirector { .isDisplaysRefreshRatesSynchronizationEnabled(); mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags .isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled(); - mVsyncProximityVoteEnabled = displayManagerFlags.isVsyncProximityVoteEnabled(); mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); mInjector = injector; @@ -208,7 +209,8 @@ public class DisplayModeDirector { mAppRequestObserver = new AppRequestObserver(); mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig()); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); - mSettingsObserver = new SettingsObserver(context, handler); + mSettingsObserver = new SettingsObserver(context, handler, mDvrrSupported, + displayManagerFlags); mBrightnessObserver = new BrightnessObserver(context, handler, injector); mDefaultDisplayDeviceConfig = null; mUdfpsObserver = new UdfpsObserver(); @@ -290,7 +292,7 @@ public class DisplayModeDirector { List<Display.Mode> availableModes = new ArrayList<>(); availableModes.add(defaultMode); VoteSummary primarySummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled, - mVsyncProximityVoteEnabled, mLoggingEnabled, mSupportsFrameRateOverride); + mDvrrSupported, mLoggingEnabled, mSupportsFrameRateOverride); int lowestConsideredPriority = Vote.MIN_PRIORITY; int highestConsideredPriority = Vote.MAX_PRIORITY; @@ -330,7 +332,7 @@ public class DisplayModeDirector { } VoteSummary appRequestSummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled, - mVsyncProximityVoteEnabled, mLoggingEnabled, mSupportsFrameRateOverride); + mDvrrSupported, mLoggingEnabled, mSupportsFrameRateOverride); appRequestSummary.applyVotes(votes, Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, @@ -818,13 +820,17 @@ public class DisplayModeDirector { private final Uri mMatchContentFrameRateSetting = Settings.Secure.getUriFor(Settings.Secure.MATCH_CONTENT_FRAME_RATE); + private final boolean mVsynLowPowerVoteEnabled; + private final Context mContext; private float mDefaultPeakRefreshRate; private float mDefaultRefreshRate; - SettingsObserver(@NonNull Context context, @NonNull Handler handler) { + SettingsObserver(@NonNull Context context, @NonNull Handler handler, boolean dvrrSupported, + DisplayManagerFlags flags) { super(handler); mContext = context; + mVsynLowPowerVoteEnabled = dvrrSupported && flags.isVsyncLowPowerVoteEnabled(); // We don't want to load from the DeviceConfig while constructing since this leads to // a spike in the latency of DisplayManagerService startup. This happens because // reading from the DeviceConfig is an intensive IO operation and having it in the @@ -936,7 +942,14 @@ public class DisplayModeDirector { boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0; final Vote vote; - if (inLowPowerMode) { + if (inLowPowerMode && mVsynLowPowerVoteEnabled) { + vote = Vote.forSupportedModes(List.of( + new SupportedModesVote.SupportedMode(/* peakRefreshRate= */ 60f, + /* vsyncRate= */ 240f), + new SupportedModesVote.SupportedMode(/* peakRefreshRate= */ 60f, + /* vsyncRate= */ 60f) + )); + } else if (inLowPowerMode) { vote = Vote.forRenderFrameRates(0f, 60f); } else { vote = null; diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java index aa44566f6d99..8f3957011b59 100644 --- a/services/core/java/com/android/server/display/mode/Vote.java +++ b/services/core/java/com/android/server/display/mode/Vote.java @@ -166,6 +166,10 @@ interface Vote { return new BaseModeRefreshRateVote(baseModeRefreshRate); } + static Vote forSupportedModes(List<SupportedModesVote.SupportedMode> supportedModes) { + return new SupportedModesVote(supportedModes); + } + static String priorityToString(int priority) { switch (priority) { case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE: diff --git a/services/core/java/com/android/server/display/mode/VoteSummary.java b/services/core/java/com/android/server/display/mode/VoteSummary.java index 99f414267dee..5fc36b589d38 100644 --- a/services/core/java/com/android/server/display/mode/VoteSummary.java +++ b/services/core/java/com/android/server/display/mode/VoteSummary.java @@ -43,14 +43,14 @@ final class VoteSummary { final boolean mIsDisplayResolutionRangeVotingEnabled; - private final boolean mVsyncProximityVoteEnabled; + private final boolean mSupportedModesVoteEnabled; private final boolean mSupportsFrameRateOverride; private final boolean mLoggingEnabled; - VoteSummary(boolean isDisplayResolutionRangeVotingEnabled, boolean vsyncProximityVoteEnabled, + VoteSummary(boolean isDisplayResolutionRangeVotingEnabled, boolean supportedModesVoteEnabled, boolean loggingEnabled, boolean supportsFrameRateOverride) { mIsDisplayResolutionRangeVotingEnabled = isDisplayResolutionRangeVotingEnabled; - mVsyncProximityVoteEnabled = vsyncProximityVoteEnabled; + mSupportedModesVoteEnabled = supportedModesVoteEnabled; mLoggingEnabled = loggingEnabled; mSupportsFrameRateOverride = supportsFrameRateOverride; reset(); @@ -253,7 +253,7 @@ final class VoteSummary { } private boolean validateModeSupported(Display.Mode mode) { - if (supportedModes == null || !mVsyncProximityVoteEnabled) { + if (supportedModes == null || !mSupportedModesVoteEnabled) { return true; } for (SupportedModesVote.SupportedMode supportedMode : supportedModes) { @@ -298,7 +298,7 @@ final class VoteSummary { return false; } - if (supportedModes != null && mVsyncProximityVoteEnabled && supportedModes.isEmpty()) { + if (supportedModes != null && mSupportedModesVoteEnabled && supportedModes.isEmpty()) { if (mLoggingEnabled) { Slog.w(TAG, "Vote summary resulted in empty set (empty supportedModes)"); } @@ -370,7 +370,7 @@ final class VoteSummary { + ", supportedModes=" + supportedModes + ", mIsDisplayResolutionRangeVotingEnabled=" + mIsDisplayResolutionRangeVotingEnabled - + ", mVsyncProximityVoteEnabled=" + mVsyncProximityVoteEnabled + + ", mSupportedModesVoteEnabled=" + mSupportedModesVoteEnabled + ", mSupportsFrameRateOverride=" + mSupportsFrameRateOverride + " }"; } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index c440a6461de2..16e043cfb64d 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -6499,10 +6499,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub nextEnabledImes).getId(); // Reset enabled IMEs. - settings.putEnabledInputMethodsStr(""); - nextEnabledImes.forEach( - imi -> settings.appendAndPutEnabledInputMethodLocked( - imi.getId())); + final String[] nextEnabledImeIds = new String[nextEnabledImes.size()]; + for (int i = 0; i < nextEnabledImeIds.length; ++i) { + nextEnabledImeIds[i] = nextEnabledImes.get(i).getId(); + } + settings.putEnabledInputMethodsStr(InputMethodUtils.concatEnabledImeIds( + settings.getEnabledInputMethodsStr(), nextEnabledImeIds)); // Reset selected IME. settings.putSelectedInputMethod(nextIme); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index 2128356e69c6..773293f83323 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -33,6 +33,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.IntArray; import android.util.Pair; import android.util.Printer; @@ -50,6 +51,8 @@ import com.android.server.textservices.TextServicesManagerInternal; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.StringJoiner; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -952,24 +955,64 @@ final class InputMethodUtils { final String enabledInputMethodsStr = TextUtils.nullIfEmpty( SecureSettingsWrapper.getString(Settings.Secure.ENABLED_INPUT_METHODS, null, userId)); - if (enabledInputMethodsStr == null) { - return List.of(); + final ArrayList<String> result = new ArrayList<>(); + splitEnabledImeStr(enabledInputMethodsStr, result::add); + return result; + } + + /** + * Split enabled IME string ({@link Settings.Secure#ENABLED_INPUT_METHODS}) into IME IDs. + * + * @param text a text formatted with {@link Settings.Secure#ENABLED_INPUT_METHODS}. + * @param consumer {@link Consumer} called back when a new IME ID is found. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + static void splitEnabledImeStr(@Nullable String text, @NonNull Consumer<String> consumer) { + if (TextUtils.isEmpty(text)) { + return; } final TextUtils.SimpleStringSplitter inputMethodSplitter = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR); final TextUtils.SimpleStringSplitter subtypeSplitter = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR); - inputMethodSplitter.setString(enabledInputMethodsStr); - final ArrayList<String> result = new ArrayList<>(); + inputMethodSplitter.setString(text); while (inputMethodSplitter.hasNext()) { String nextImsStr = inputMethodSplitter.next(); subtypeSplitter.setString(nextImsStr); if (subtypeSplitter.hasNext()) { // The first element is ime id. - result.add(subtypeSplitter.next()); + consumer.accept(subtypeSplitter.next()); } } - return result; + } + + /** + * Concat given IME IDs with an existing enabled IME + * ({@link Settings.Secure#ENABLED_INPUT_METHODS}). + * + * @param existingEnabledImeId an existing {@link Settings.Secure#ENABLED_INPUT_METHODS} to + * which {@code imeIDs} will be added. + * @param imeIds an array of IME IDs to be added. For IME IDs that are already seen in + * {@code existingEnabledImeId} will be skipped. + * @return a new enabled IME ID string that can be stored in + * {@link Settings.Secure#ENABLED_INPUT_METHODS}. + */ + @NonNull + static String concatEnabledImeIds(@NonNull String existingEnabledImeId, + @NonNull String... imeIds) { + final ArraySet<String> alreadyEnabledIds = new ArraySet<>(); + final StringJoiner joiner = new StringJoiner(Character.toString(INPUT_METHOD_SEPARATOR)); + if (!TextUtils.isEmpty(existingEnabledImeId)) { + splitEnabledImeStr(existingEnabledImeId, alreadyEnabledIds::add); + joiner.add(existingEnabledImeId); + } + for (String id : imeIds) { + if (!alreadyEnabledIds.contains(id)) { + joiner.add(id); + alreadyEnabledIds.add(id); + } + } + return joiner.toString(); } /** diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index df9e7417054b..5e18727459c6 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -964,6 +964,7 @@ class MediaRouter2ServiceImpl { disposeUserIfNeededLocked(userRecord); // since router removed from user } + @GuardedBy("mLock") private void setDiscoveryRequestWithRouter2Locked(@NonNull RouterRecord routerRecord, @NonNull RouteDiscoveryPreference discoveryRequest) { if (routerRecord.mDiscoveryPreference.equals(discoveryRequest)) { @@ -1016,6 +1017,7 @@ class MediaRouter2ServiceImpl { routeListingPreference)); } + @GuardedBy("mLock") private void setRouteVolumeWithRouter2Locked(@NonNull IMediaRouter2 router, @NonNull MediaRoute2Info route, int volume) { final IBinder binder = router.asBinder(); @@ -1035,6 +1037,7 @@ class MediaRouter2ServiceImpl { } } + @GuardedBy("mLock") private void requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId, @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) { @@ -1117,6 +1120,7 @@ class MediaRouter2ServiceImpl { sessionHints)); } + @GuardedBy("mLock") private void selectRouteWithRouter2Locked(@NonNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { final IBinder binder = router.asBinder(); @@ -1138,6 +1142,7 @@ class MediaRouter2ServiceImpl { DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); } + @GuardedBy("mLock") private void deselectRouteWithRouter2Locked(@NonNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { final IBinder binder = router.asBinder(); @@ -1159,6 +1164,7 @@ class MediaRouter2ServiceImpl { DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); } + @GuardedBy("mLock") private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { final IBinder binder = router.asBinder(); @@ -1191,6 +1197,7 @@ class MediaRouter2ServiceImpl { } } + @GuardedBy("mLock") private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router, @NonNull String uniqueSessionId, int volume) { final IBinder binder = router.asBinder(); @@ -1215,6 +1222,7 @@ class MediaRouter2ServiceImpl { DUMMY_REQUEST_ID, uniqueSessionId, volume)); } + @GuardedBy("mLock") private void releaseSessionWithRouter2Locked(@NonNull IMediaRouter2 router, @NonNull String uniqueSessionId) { final IBinder binder = router.asBinder(); @@ -1240,6 +1248,7 @@ class MediaRouter2ServiceImpl { // Start of locked methods that are used by MediaRouter2Manager. + @GuardedBy("mLock") private List<RoutingSessionInfo> getRemoteSessionsLocked( @NonNull IMediaRouter2Manager manager) { final IBinder binder = manager.asBinder(); @@ -1330,6 +1339,7 @@ class MediaRouter2ServiceImpl { UserHandler::notifyInitialRoutesToManager, userRecord.mHandler, manager)); } + @GuardedBy("mLock") private void unregisterManagerLocked(@NonNull IMediaRouter2Manager manager, boolean died) { ManagerRecord managerRecord = mAllManagerRecords.remove(manager.asBinder()); if (managerRecord == null) { @@ -1355,6 +1365,7 @@ class MediaRouter2ServiceImpl { disposeUserIfNeededLocked(userRecord); // since manager removed from user } + @GuardedBy("mLock") private void startScanLocked(@NonNull IMediaRouter2Manager manager) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -1368,6 +1379,7 @@ class MediaRouter2ServiceImpl { managerRecord.startScan(); } + @GuardedBy("mLock") private void stopScanLocked(@NonNull IMediaRouter2Manager manager) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -1381,6 +1393,7 @@ class MediaRouter2ServiceImpl { managerRecord.stopScan(); } + @GuardedBy("mLock") private void setRouteVolumeWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull MediaRoute2Info route, int volume) { @@ -1402,6 +1415,7 @@ class MediaRouter2ServiceImpl { uniqueRequestId, route, volume)); } + @GuardedBy("mLock") private void requestCreateSessionWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) { @@ -1455,6 +1469,7 @@ class MediaRouter2ServiceImpl { uniqueRequestId, routerRecord, managerRecord, oldSession, route)); } + @GuardedBy("mLock") private void selectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { final IBinder binder = manager.asBinder(); @@ -1479,6 +1494,7 @@ class MediaRouter2ServiceImpl { uniqueRequestId, routerRecord, uniqueSessionId, route)); } + @GuardedBy("mLock") private void deselectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { @@ -1504,6 +1520,7 @@ class MediaRouter2ServiceImpl { uniqueRequestId, routerRecord, uniqueSessionId, route)); } + @GuardedBy("mLock") private void transferToRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) { @@ -1529,6 +1546,7 @@ class MediaRouter2ServiceImpl { uniqueRequestId, routerRecord, uniqueSessionId, route)); } + @GuardedBy("mLock") private void setSessionVolumeWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId, int volume) { @@ -1550,6 +1568,7 @@ class MediaRouter2ServiceImpl { uniqueRequestId, uniqueSessionId, volume)); } + @GuardedBy("mLock") private void releaseSessionWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull String uniqueSessionId) { final IBinder binder = manager.asBinder(); @@ -1652,6 +1671,7 @@ class MediaRouter2ServiceImpl { // TODO: This assumes that only one router exists in a package. // Do this in Android S or later. + @GuardedBy("mLock") RouterRecord findRouterRecordLocked(String packageName) { for (RouterRecord routerRecord : mRouterRecords) { if (TextUtils.equals(routerRecord.mPackageName, packageName)) { @@ -1739,6 +1759,7 @@ class MediaRouter2ServiceImpl { return mHasModifyAudioRoutingPermission || mHasBluetoothRoutingPermission.get(); } + @GuardedBy("mLock") public void maybeUpdateSystemRoutingPermissionLocked() { boolean oldSystemRoutingPermissionValue = hasSystemRoutingPermission(); mHasBluetoothRoutingPermission.set(checkCallerHasBluetoothPermissions(mPid, mUid)); @@ -2063,6 +2084,7 @@ class MediaRouter2ServiceImpl { this, provider, uniqueRequestId, reason)); } + @GuardedBy("mLock") @Nullable public RouterRecord findRouterWithSessionLocked(@NonNull String uniqueSessionId) { return mSessionToRouterMap.get(uniqueSessionId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index b12180bffdcd..4b8de4e8c6f1 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -19,6 +19,7 @@ package com.android.server.notification; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.service.notification.DeviceEffectsApplier; import java.util.Set; @@ -54,4 +55,22 @@ public interface NotificationManagerInternal { void cleanupHistoryFiles(); void removeBitmaps(); + + /** + * Sets the {@link DeviceEffectsApplier} that will be used to apply the different + * {@link android.service.notification.ZenDeviceEffects} that are relevant for the platform + * when {@link android.service.notification.ZenModeConfig.ZenRule} instances are activated and + * deactivated. + * + * <p>This method is optional and needs only be called if the platform supports non-standard + * effects (i.e. any that are not <em>public APIs</em> in + * {@link android.service.notification.ZenDeviceEffects}, or if they must be applied in a + * non-standard fashion. If not used, a {@link DefaultDeviceEffectsApplier} will be invoked, + * which should be sufficient for most devices. + * + * <p>If this method is called, it <em>must</em> be during system startup and <em>before</em> + * the {@link com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START} boot phase. + * Otherwise an {@link IllegalStateException} will be thrown. + */ + void setDeviceEffectsApplier(DeviceEffectsApplier applier); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 66a974080a43..e7ae61072db4 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -251,6 +251,7 @@ import android.provider.Settings.Secure; import android.service.notification.Adjustment; import android.service.notification.Condition; import android.service.notification.ConversationChannelWrapper; +import android.service.notification.DeviceEffectsApplier; import android.service.notification.IConditionProvider; import android.service.notification.INotificationListener; import android.service.notification.IStatusBarNotificationHolder; @@ -6964,6 +6965,18 @@ public class NotificationManagerService extends SystemService { } } } + + @Override + public void setDeviceEffectsApplier(DeviceEffectsApplier applier) { + if (!android.app.Flags.modesApi()) { + return; + } + if (mZenModeHelper == null) { + throw new IllegalStateException("ZenModeHelper is not yet ready!"); + } + // This can also throw IllegalStateException if called too late. + mZenModeHelper.setDeviceEffectsApplier(applier); + } }; private static boolean isBigPictureWithBitmapOrIcon(Notification n) { diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java index 7b35589ae682..7f58e75e0287 100644 --- a/services/core/java/com/android/server/pm/BroadcastHelper.java +++ b/services/core/java/com/android/server/pm/BroadcastHelper.java @@ -837,13 +837,11 @@ public final class BroadcastHelper { } final String removedPackage = packageRemovedInfo.mRemovedPackage; - final int removedAppId = packageRemovedInfo.mRemovedAppId; - final int uid = packageRemovedInfo.mUid; final String installerPackageName = packageRemovedInfo.mInstallerPackageName; final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList; Bundle extras = new Bundle(2); - extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); + extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid); extras.putBoolean(Intent.EXTRA_REPLACING, true); sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null); @@ -888,8 +886,6 @@ public final class BroadcastHelper { boolean removedBySystem, boolean isArchived) { final String removedPackage = packageRemovedInfo.mRemovedPackage; - final int removedAppId = packageRemovedInfo.mRemovedAppId; - final int uid = packageRemovedInfo.mUid; final String installerPackageName = packageRemovedInfo.mInstallerPackageName; final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers; final int[] instantUserIds = packageRemovedInfo.mInstantUserIds; @@ -902,8 +898,7 @@ public final class BroadcastHelper { final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib; Bundle extras = new Bundle(); - final int removedUid = removedAppId >= 0 ? removedAppId : uid; - extras.putInt(Intent.EXTRA_UID, removedUid); + extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved); extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate); extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp); @@ -940,10 +935,10 @@ public final class BroadcastHelper { sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, null, broadcastUserIds, instantUserIds, broadcastAllowList, null); - packageSender.notifyPackageRemoved(removedPackage, removedUid); + packageSender.notifyPackageRemoved(removedPackage, packageRemovedInfo.mUid); } } - if (removedAppId >= 0) { + if (packageRemovedInfo.mIsAppIdRemoved) { // If a system app's updates are uninstalled the UID is not actually removed. Some // services need to know the package name affected. if (isReplace) { diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index 27f4e11c53ad..482807c397ea 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -518,7 +518,9 @@ public interface Computer extends PackageDataSnapshot { boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) throws PackageManager.NameNotFoundException; - boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId); + /** Check if the package is suspending any package. */ + boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, + @UserIdInt int suspendingUserId, int targetUserId); @NonNull ParceledListSlice<IntentFilter> getAllIntentFilters(@NonNull String packageName); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index abfd5715810e..3cb2420cd223 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -96,6 +96,7 @@ import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.content.pm.SigningInfo; import android.content.pm.UserInfo; +import android.content.pm.UserPackage; import android.content.pm.VersionedPackage; import android.os.Binder; import android.os.Build; @@ -3864,19 +3865,15 @@ public class ComputerEngine implements Computer { } finally { Binder.restoreCallingIdentity(identity); } - - var usingSharedLibraryPair = - getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId); SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(), libInfo.getPackageName(), libInfo.getAllCodePaths(), libInfo.getName(), libInfo.getLongVersion(), libInfo.getType(), declaringPackage, - usingSharedLibraryPair.first, (libInfo.getDependencies() == null ? null : new ArrayList<>(libInfo.getDependencies())), - libInfo.isNative()); - + libInfo.isNative(), + getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId)); if (result == null) { result = new ArrayList<>(); } @@ -5012,11 +5009,13 @@ public class ComputerEngine implements Computer { @Override public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, - @UserIdInt int userId) { + @UserIdInt int suspendingUserId, int targetUserId) { + final UserPackage suspender = UserPackage.of(suspendingUserId, suspendingPackage); for (final PackageStateInternal packageState : getPackageStates().values()) { - final PackageUserStateInternal state = packageState.getUserStateOrDefault(userId); + final PackageUserStateInternal state = + packageState.getUserStateOrDefault(targetUserId); if (state.getSuspendParams() != null - && state.getSuspendParams().containsKey(suspendingPackage)) { + && state.getSuspendParams().containsKey(suspender)) { return true; } } diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index dcf921c90885..aa7f0d3c668a 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -510,7 +510,10 @@ final class DeletePackageHelper { } if (clearPackageStateAndReturn) { mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, flags); - outInfo.mRemovedAppId = ps.getAppId(); + // Legacy behavior to report appId as UID here. + // The final broadcasts will contain a per-user UID. + outInfo.mUid = ps.getAppId(); + outInfo.mIsAppIdRemoved = true; mPm.scheduleWritePackageRestrictions(user); return; } @@ -555,6 +558,7 @@ final class DeletePackageHelper { boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles, @NonNull PackageRemovedInfo outInfo, boolean writeSettings) { synchronized (mPm.mLock) { + // Since the package is being deleted in all users, report appId as the uid outInfo.mUid = ps.getAppId(); outInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList( mPm.snapshotComputer(), ps, allUserHandles, diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 6480d6483062..0fa7aa5473b2 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -2912,7 +2912,8 @@ final class InstallPackageHelper { info.mInstallerPackageName = request.getInstallerPackageName(); info.mRemovedUsers = firstUserIds; info.mBroadcastUsers = firstUserIds; - info.mRemovedAppId = request.getAppId(); + info.mUid = request.getAppId(); + info.mIsAppIdRemoved = true; info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode(); info.mRemovedForAllUsers = true; diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 9a51cc0e26ba..ee780d99b6b6 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -818,7 +818,8 @@ final class InstallRequest { public void setRemovedAppId(int appId) { if (mRemovedInfo != null) { - mRemovedInfo.mRemovedAppId = appId; + mRemovedInfo.mUid = appId; + mRemovedInfo.mIsAppIdRemoved = true; } } diff --git a/services/core/java/com/android/server/pm/KnownPackages.java b/services/core/java/com/android/server/pm/KnownPackages.java index 154709a62095..83831ca61aa1 100644 --- a/services/core/java/com/android/server/pm/KnownPackages.java +++ b/services/core/java/com/android/server/pm/KnownPackages.java @@ -77,6 +77,8 @@ public final class KnownPackages { // Please note the numbers should be continuous. public static final int LAST_KNOWN_PACKAGE = PACKAGE_WEARABLE_SENSING; + static final String SYSTEM_PACKAGE_NAME = "android"; + private final DefaultAppProvider mDefaultAppProvider; private final String mRequiredInstallerPackage; private final String mRequiredUninstallerPackage; @@ -186,7 +188,7 @@ public final class KnownPackages { case PACKAGE_SETUP_WIZARD: return snapshot.filterOnlySystemPackages(mSetupWizardPackage); case PACKAGE_SYSTEM: - return new String[]{"android"}; + return new String[]{SYSTEM_PACKAGE_NAME}; case PACKAGE_VERIFIER: return snapshot.filterOnlySystemPackages(mRequiredVerifierPackages); case PACKAGE_SYSTEM_TEXT_CLASSIFIER: diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index ba66377beb8a..127bf495d2ac 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -822,7 +822,7 @@ public class LauncherAppsService extends SystemService { return new LauncherActivityInfoInternal( activityInfo, new IncrementalStatesInfo( - false /* isLoading */, 1 /* progress */, 0 /* loadingCompletedTime */), + false /* isLoading */, 0 /* progress */, 0 /* loadingCompletedTime */), user); } @@ -1599,6 +1599,33 @@ public class LauncherAppsService extends SystemService { } @Override + public @Nullable IntentSender getAppMarketActivityIntent(@NonNull String callingPackage, + @Nullable String packageName, @NonNull UserHandle user) { + // Only system launchers, which have access to recents should have access to this API. + // TODO(b/303803157): Update access control for this API to default Launcher app. + if (!mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) { + throw new SecurityException("Caller is not the recents app"); + } + if (!canAccessProfile(user.getIdentifier(), + "Can't access AppMarketActivity for another user")) { + return null; + } + final long identity = Binder.clearCallingIdentity(); + try { + // TODO(b/316118005): Add code to launch the app installer for the packageName. + Intent appMarketIntent = new Intent(Intent.ACTION_MAIN); + appMarketIntent.addCategory(Intent.CATEGORY_APP_MARKET); + final PendingIntent pi = PendingIntent.getActivityAsUser( + mContext, /* requestCode */ 0, appMarketIntent, PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT, + /* options */ null, user); + return pi == null ? null : pi.getIntentSender(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void startActivityAsUser(IApplicationThread caller, String callingPackage, String callingFeatureId, ComponentName component, Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException { diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index 6b05edf7c25a..2864a8b42445 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -165,7 +165,7 @@ public class PackageArchiver { Computer snapshot = mPm.snapshotComputer(); int userId = userHandle.getIdentifier(); int binderUid = Binder.getCallingUid(); - if (!PackageManagerServiceUtils.isRootOrShell(binderUid)) { + if (!PackageManagerServiceUtils.isSystemOrRootOrShell(binderUid)) { verifyCaller(snapshot.getPackageUid(callerPackageName, 0, userId), binderUid); } snapshot.enforceCrossUserPermission(binderUid, userId, true, true, diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 7d6dd62153c1..2942bbb86e62 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -821,6 +821,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements params.installFlags &= ~PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK; } + params.installFlags &= ~PackageManager.INSTALL_UNARCHIVE; + if (Flags.archiving() && params.appPackageName != null) { + PackageStateInternal ps = mPm.snapshotComputer().getPackageStateInternal( + params.appPackageName, SYSTEM_UID); + if (ps != null + && PackageArchiver.isArchived(ps.getUserStateOrDefault(userId)) + && PackageArchiver.getResponsibleInstallerPackage(ps) + .equals(requestedInstallerPackageName)) { + params.installFlags |= PackageManager.INSTALL_UNARCHIVE; + } + } + if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0 && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid) && (snapshot.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM) diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index c737b45ae885..8da168375447 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -19,6 +19,8 @@ package com.android.server.pm; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.RESTRICTION_NONE; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -37,6 +39,7 @@ import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.SuspendDialogInfo; +import android.content.pm.UserPackage; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -250,7 +253,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { getSuspendPackageHelper().removeSuspensionsBySuspendingPackage(snapshot(), new String[]{packageName}, (suspendingPackage) -> !PackageManagerService.PLATFORM_PACKAGE_NAME.equals( - suspendingPackage), + suspendingPackage.packageName), userId); } @@ -269,7 +272,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated - public final String getSuspendingPackage(String suspendedPackage, int userId) { + public final UserPackage getSuspendingPackage(String suspendedPackage, int userId) { return getSuspendPackageHelper().getSuspendingPackage(snapshot(), suspendedPackage, userId, Binder.getCallingUid()); } @@ -277,7 +280,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated public final SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, - String suspendingPackage, int userId) { + UserPackage suspendingPackage, int userId) { return getSuspendPackageHelper().getSuspendedDialogInfo(snapshot(), suspendedPackage, suspendingPackage, userId, Binder.getCallingUid()); } @@ -683,14 +686,16 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated - public final void unsuspendForSuspendingPackage(final String packageName, int affectedUser) { - mService.unsuspendForSuspendingPackage(snapshot(), packageName, affectedUser); + public final void unsuspendAdminSuspendedPackages(int affectedUser) { + final int suspendingUserId = affectedUser; + mService.unsuspendForSuspendingPackage(snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId); } @Override @Deprecated - public final boolean isSuspendingAnyPackages(String suspendingPackage, int userId) { - return snapshot().isSuspendingAnyPackages(suspendingPackage, userId); + public final boolean isAdminSuspendingAnyPackages(int userId) { + final int suspendingUserId = userId; + return snapshot().isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, suspendingUserId, userId); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 56365b676618..bc441b84c58b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -292,6 +292,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Predicate; /** * Keep track of all those APKs everywhere. @@ -3137,7 +3138,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot, - boolean quarantined, String callingPackage, int callingUid, int userId, + boolean quarantined, UserPackage suspender, int callingUid, int targetUserId, String callingMethod) { if (callingUid == Process.ROOT_UID // Need to compare app-id to allow system dialogs access on secondary users @@ -3145,9 +3146,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService return; } - final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId); + final String ownerPackage = + mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId); if (ownerPackage != null) { - final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, userId); + final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, targetUserId); if (ownerUid == callingUid) { return; } @@ -3168,25 +3170,27 @@ public class PackageManagerService implements PackageSender, TestUtilityService callingMethod); } - final int packageUid = snapshot.getPackageUid(callingPackage, 0, userId); + final int packageUid = snapshot.getPackageUid(suspender.packageName, 0, targetUserId); final boolean allowedPackageUid = packageUid == callingUid; // TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL final boolean allowedShell = callingUid == SHELL_UID && UserHandle.isSameApp(packageUid, callingUid); if (!allowedShell && !allowedPackageUid) { - throw new SecurityException("Calling package " + callingPackage + " in user " - + userId + " does not belong to calling uid " + callingUid); + throw new SecurityException("Suspending package " + suspender.packageName + + " in user " + targetUserId + " does not belong to calling uid " + callingUid); } } void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage, - @UserIdInt int userId) { + @UserIdInt int suspendingUserId) { // TODO: This can be replaced by a special parameter to iterate all packages, rather than // this weird pre-collect of all packages. final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]); + final Predicate<UserPackage> suspenderPredicate = + UserPackage.of(suspendingUserId, suspendingPackage)::equals; mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, - allPackages, suspendingPackage::equals, userId); + allPackages, suspenderPredicate, suspendingUserId); } void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) { @@ -5259,8 +5263,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (!snapshot.isPackageSuspendedForUser(packageName, userId)) { return null; } - return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId, - callingUid); + final UserPackage suspender = mSuspendPackageHelper.getSuspendingPackage( + snapshot, packageName, userId, callingUid); + return suspender != null ? suspender.packageName : null; } catch (PackageManager.NameNotFoundException e) { return null; } @@ -6198,7 +6203,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, - SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) { + SuspendDialogInfo dialogInfo, int flags, String suspendingPackage, + int suspendingUserId, int targetUserId) { final int callingUid = Binder.getCallingUid(); boolean quarantined = false; if (Flags.quarantinedEnabled()) { @@ -6207,14 +6213,15 @@ public class PackageManagerService implements PackageSender, TestUtilityService } else if (FeatureFlagUtils.isEnabled(mContext, SETTINGS_TREAT_PAUSE_AS_QUARANTINE)) { final String wellbeingPkg = mContext.getString(R.string.config_systemWellbeing); - quarantined = callingPackage.equals(wellbeingPkg); + quarantined = suspendingPackage.equals(wellbeingPkg); } } final Computer snapshot = snapshotComputer(); - enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, callingPackage, callingUid, - userId, "setPackagesSuspendedAsUser"); + final UserPackage suspender = UserPackage.of(targetUserId, suspendingPackage); + enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, suspender, callingUid, + targetUserId, "setPackagesSuspendedAsUser"); return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended, - appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid, + appExtras, launcherExtras, dialogInfo, suspender, targetUserId, callingUid, quarantined); } @@ -6653,7 +6660,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService final Computer computer = snapshotComputer(); final String[] allPackages = computer.getAllAvailablePackageNames(); mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages, - (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage), + (suspender) -> !PLATFORM_PACKAGE_NAME.equals(suspender.packageName), userId); } @@ -6667,8 +6674,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String[] setPackagesSuspendedByAdmin( @UserIdInt int userId, @NonNull String[] packageNames, boolean suspended) { - return mSuspendPackageHelper.setPackagesSuspendedByAdmin( - snapshotComputer(), userId, packageNames, suspended); + final int suspendingUserId = userId; + final UserPackage suspender = UserPackage.of( + suspendingUserId, PackageManagerService.PLATFORM_PACKAGE_NAME); + return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames, + suspended, null /* appExtras */, null /* launcherExtras */, + null /* dialogInfo */, suspender, userId, Process.SYSTEM_UID, + false /* quarantined */); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 215e9528a35e..243fb16b19ae 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2827,7 +2827,7 @@ class PackageManagerShellCommand extends ShellCommand { mInterface.setPackagesSuspendedAsUser(packageNames.toArray(new String[] {}), suspendedState, ((appExtras.size() > 0) ? appExtras : null), ((launcherExtras.size() > 0) ? launcherExtras : null), - info, flags, callingPackage, translatedUserId); + info, flags, callingPackage, UserHandle.USER_SYSTEM, translatedUserId); for (int i = 0; i < packageNames.size(); i++) { final String packageName = packageNames.get(i); pw.println("Package " + packageName + " new suspended state: " @@ -2872,16 +2872,16 @@ class PackageManagerShellCommand extends ShellCommand { UserHandle.USER_NULL, "runGrantRevokePermission")); List<PackageInfo> packageInfos; + PackageManager pm = mContext.createContextAsUser(translatedUser, 0).getPackageManager(); if (pkg == null) { - packageInfos = mContext.getPackageManager().getInstalledPackages( - PackageManager.GET_PERMISSIONS); + packageInfos = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS); } else { try { - packageInfos = Collections.singletonList( - mContext.getPackageManager().getPackageInfo(pkg, - PackageManager.GET_PERMISSIONS)); + packageInfos = Collections.singletonList(pm.getPackageInfo(pkg, + PackageManager.GET_PERMISSIONS)); } catch (NameNotFoundException e) { getErrPrintWriter().println("Error: package not found"); + getOutPrintWriter().println("Failure [package not found]"); return 1; } } diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java index 7ee1772adead..881b0b398f9a 100644 --- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java +++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java @@ -25,7 +25,7 @@ final class PackageRemovedInfo { String mRemovedPackage; String mInstallerPackageName; int mUid = -1; - int mRemovedAppId = -1; + boolean mIsAppIdRemoved = false; int[] mOrigUsers; int[] mRemovedUsers = null; int[] mBroadcastUsers = null; diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 7d0a1f6afe1d..28a90f3d6ab6 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -32,6 +32,7 @@ import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; import android.content.pm.SigningInfo; import android.content.pm.UserInfo; +import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.os.UserHandle; import android.os.incremental.IncrementalManager; @@ -952,7 +953,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal void setUserState(int userId, long ceDataInode, long deDataInode, int enabled, boolean installed, boolean stopped, boolean notLaunched, boolean hidden, - int distractionFlags, ArrayMap<String, SuspendParams> suspendParams, + int distractionFlags, ArrayMap<UserPackage, SuspendParams> suspendParams, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, ArraySet<String> enabledComponents, ArraySet<String> disabledComponents, int installReason, int uninstallReason, @@ -1182,7 +1183,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal if (state.isSuspended()) { for (int j = 0; j < state.getSuspendParams().size(); j++) { proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, - state.getSuspendParams().keyAt(j)); + state.getSuspendParams().keyAt(j).packageName); } } proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.isStopped()); diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index 981f24bc179a..b6de0e5c030f 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -20,15 +20,19 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIB import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY; +import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX; import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; +import static com.android.server.pm.PackageManagerService.TAG; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; +import android.os.Build; import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Log; +import android.util.Slog; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; @@ -49,6 +53,8 @@ import java.util.Map; * as install) led to the request. */ final class ReconcilePackageUtils { + private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE = Build.IS_DEBUGGABLE || true; + public static List<ReconciledPackage> reconcilePackages( List<InstallRequest> installRequests, Map<String, AndroidPackage> allPackages, @@ -90,6 +96,8 @@ final class ReconcilePackageUtils { } } + final AndroidPackage systemPackage = allPackages.get(KnownPackages.SYSTEM_PACKAGE_NAME); + for (InstallRequest installRequest : installRequests) { final String installPackageName = installRequest.getParsedPackage().getPackageName(); final List<SharedLibraryInfo> allowedSharedLibInfos = @@ -133,6 +141,9 @@ final class ReconcilePackageUtils { if (parsedPackage != null) { signingDetails = parsedPackage.getSigningDetails(); } + final boolean isSystemPackage = + ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0); + final boolean isApex = (scanFlags & SCAN_AS_APEX) != 0; SharedUserSetting sharedUserSetting = settings.getSharedUserSettingLPr( signatureCheckPs); if (ksms.shouldCheckUpgradeKeySetLocked( @@ -141,7 +152,7 @@ final class ReconcilePackageUtils { // We just determined the app is signed correctly, so bring // over the latest parsed certs. } else { - if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) { + if (!isSystemPackage) { throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + parsedPackage.getPackageName() + " upgrade keys do not match the previously installed" @@ -168,9 +179,23 @@ final class ReconcilePackageUtils { removeAppKeySetData = true; } + if (!isSystemPackage && !isApex && signingDetails != null + && systemPackage != null && systemPackage.getSigningDetails() != null + && systemPackage.getSigningDetails().checkCapability( + signingDetails, + SigningDetails.CertCapabilities.PERMISSION)) { + Slog.d(TAG, "Non-preload app associated with system signature: " + + signatureCheckPs.getPackageName()); + if (!ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE) { + throw new ReconcileFailure( + INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, + "Non-preload app associated with system signature: " + + signatureCheckPs.getPackageName()); + } + } + // if this is is a sharedUser, check to see if the new package is signed by a - // newer - // signing certificate than the existing one, and if so, copy over the new + // newer signing certificate than the existing one, and if so, copy over the new // details if (sharedUserSetting != null) { // Attempt to merge the existing lineage for the shared SigningDetails with @@ -203,7 +228,7 @@ final class ReconcilePackageUtils { } } } catch (PackageManagerException e) { - if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) { + if (!isSystemPackage) { throw new ReconcileFailure(e); } signingDetails = parsedPackage.getSigningDetails(); diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index 8ff4a6dd9440..f42b43f5aeef 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -252,40 +252,103 @@ final class RemovePackageHelper { } } + /** + * This method clears the data and states stored in the system that are related to the + * package being deleted and the target user, including the data directory. + * If the DELETE_KEEP_DATA flag is set, everything is preserved except ART profiles. + * Make sure this flag is set for partially installed apps. If not it's meaningless to + * delete a partially installed application. + */ public void clearPackageStateForUserLIF(PackageSetting ps, int userId, int flags) { + final String packageName = ps.getPackageName(); + // Step 1: always destroy app profiles. + mAppDataHelper.destroyAppProfilesLIF(packageName); + + // Everything else is preserved if the DELETE_KEEP_DATA flag is on + if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) { + return; + } + final AndroidPackage pkg; final SharedUserSetting sus; synchronized (mPm.mLock) { - pkg = mPm.mPackages.get(ps.getPackageName()); + pkg = mPm.mPackages.get(packageName); sus = mPm.mSettings.getSharedUserSettingLPr(ps); } - mAppDataHelper.destroyAppProfilesLIF(ps.getPackageName()); + final AndroidPackage resolvedPkg; + if (pkg != null) { + resolvedPkg = pkg; + } else { + // We don't have a parsed package when it lives on an ejected + // adopted storage device, so fake something together + resolvedPkg = PackageImpl.buildFakeForDeletion(packageName, ps.getVolumeUuid()); + } + + // Step 2: destroy app data. + mAppDataHelper.destroyAppDataLIF(resolvedPkg, userId, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); + if (userId != UserHandle.USER_ALL) { + ps.setCeDataInode(-1, userId); + ps.setDeDataInode(-1, userId); + } - final List<AndroidPackage> sharedUserPkgs = - sus != null ? sus.getPackages() : Collections.emptyList(); final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm, mBroadcastHelper); - final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds() - : new int[] {userId}; - for (int nextUserId : userIds) { + if (userId == UserHandle.USER_ALL) { if (DEBUG_REMOVE) { - Slog.d(TAG, "Updating package:" + ps.getPackageName() + " install state for user:" - + nextUserId); + Slog.d(TAG, "Clear package:" + packageName + " state for all users"); } - if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { - mAppDataHelper.destroyAppDataLIF(pkg, nextUserId, - FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); - ps.setCeDataInode(-1, nextUserId); - ps.setDeDataInode(-1, nextUserId); + // Step 3: inform DomainVerificationManager. + mPm.mDomainVerificationManager.clearPackage(packageName); + synchronized (mPm.mLock) { + // Step 3.1 (only for USER_ALL): notify KeySetManagerService. + mPm.mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); + // Step 3.2 (only for USER_ALL): update installer ownership. + mPm.mInjector.getUpdateOwnershipHelper().removeUpdateOwnerDenyList(packageName); + // Step 3.3 (only for USER_ALL): update AppsFilter. + final Computer snapshot = mPm.snapshotComputer(); + mPm.mAppsFilter.removePackage(snapshot, + snapshot.getPackageStateInternal(packageName)); + // Step 4: clear perferred activities. + final SparseBooleanArray changedUsers = new SparseBooleanArray(); + mPm.clearPackagePreferredActivitiesLPw( + packageName, changedUsers, UserHandle.USER_ALL); + mPm.mInjector.getBackgroundHandler().post(() -> { + if (changedUsers.size() > 0) { + preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(), + changedUsers); + mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL); + } + }); + // Step 5: inform PermissionManager. + // This has to be done after the removal from mSettings in removePackageDataLIF. + } + } else { + if (DEBUG_REMOVE) { + Slog.d(TAG, "Clear package:" + packageName + " state for user:" + userId); } - mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId()); - preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(), - nextUserId); - mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId); + // Step 3: inform DomainVerificationManager. + mPm.mDomainVerificationManager.clearPackageForUser(packageName, userId); + // Step 4: clear perferred activities. + preferredActivityHelper.clearPackagePreferredActivities(packageName, userId); + // Step 5: inform PermissionManager. + List<AndroidPackage> sharedUserPkgs = + sus != null ? sus.getPackages() : Collections.emptyList(); + mPermissionManager.onPackageUninstalled(packageName, ps.getAppId(), ps, pkg, + sharedUserPkgs, userId); } - mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), ps, pkg, - sharedUserPkgs, userId); + + // Step 6: detroy keystore data. + mPm.mInjector.getBackgroundHandler().post(() -> { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, + "clearKeystoreData:" + ps.getAppId() + " for user: " + userId); + mAppDataHelper.clearKeystoreData(userId, ps.getAppId()); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + }); } // Called to clean up disabled system packages @@ -297,10 +360,7 @@ final class RemovePackageHelper { } /* - * This method deletes the package from internal data structures. If the DELETE_KEEP_DATA - * flag is not set, the data directory is removed as well. - * make sure this flag is set for partially installed apps. If not it's meaningless to - * delete a partially installed application. + * This method deletes the package from internal data structures such as mPackages / mSettings. */ @GuardedBy("mPm.mInstallLock") public void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles, @@ -310,7 +370,11 @@ final class RemovePackageHelper { // Retrieve object to delete permissions for shared user later on final AndroidPackage deletedPkg = deletedPs.getPkg(); - removePackageLI(deletedPs.getPackageName(), (flags & PackageManager.DELETE_CHATTY) != 0); + // Delete all the data and states related to this package. + clearPackageStateForUserLIF(deletedPs, UserHandle.USER_ALL, flags); + + // Delete from mPackages + removePackageLI(packageName, (flags & PackageManager.DELETE_CHATTY) != 0); if (!deletedPs.isSystem()) { // A non-system app's AndroidPackage object has been removed from the service. // Explicitly nullify the corresponding app's PackageSetting's pkg object to @@ -320,40 +384,16 @@ final class RemovePackageHelper { } if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { - final AndroidPackage resolvedPkg; - if (deletedPkg != null) { - resolvedPkg = deletedPkg; - } else { - // We don't have a parsed package when it lives on an ejected - // adopted storage device, so fake something together - resolvedPkg = PackageImpl.buildFakeForDeletion(deletedPs.getPackageName(), - deletedPs.getVolumeUuid()); - } - mAppDataHelper.destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL, - FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); - mAppDataHelper.destroyAppProfilesLIF(resolvedPkg.getPackageName()); - } - - int removedAppId = -1; - - // writer - if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { + // Delete from mSettings final SparseBooleanArray changedUsers = new SparseBooleanArray(); synchronized (mPm.mLock) { - mPm.mDomainVerificationManager.clearPackage(deletedPs.getPackageName()); - mPm.mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName); - mPm.mInjector.getUpdateOwnershipHelper().removeUpdateOwnerDenyList(packageName); - final Computer snapshot = mPm.snapshotComputer(); - mPm.mAppsFilter.removePackage(snapshot, - snapshot.getPackageStateInternal(packageName)); - removedAppId = mPm.mSettings.removePackageLPw(packageName); - outInfo.mRemovedAppId = removedAppId; - + mPm.mSettings.removePackageLPw(packageName); + outInfo.mIsAppIdRemoved = true; if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) { + final SharedUserSetting sus = mPm.mSettings.getSharedUserSettingLPr(deletedPs); // If we don't have a disabled system package to reinstall, the package is // really gone and its permission state should be removed. - SharedUserSetting sus = mPm.mSettings.getSharedUserSettingLPr(deletedPs); - List<AndroidPackage> sharedUserPkgs = + final List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : Collections.emptyList(); mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(), deletedPs, deletedPkg, sharedUserPkgs, UserHandle.USER_ALL); @@ -362,20 +402,8 @@ final class RemovePackageHelper { mPm.mSettings.checkAndConvertSharedUserSettingsLPw(sus); } } - mPm.clearPackagePreferredActivitiesLPw( - deletedPs.getPackageName(), changedUsers, UserHandle.USER_ALL); - mPm.mSettings.removeRenamedPackageLPw(deletedPs.getRealName()); } - if (changedUsers.size() > 0) { - mPm.mInjector.getBackgroundHandler().post(() -> { - final PreferredActivityHelper preferredActivityHelper = - new PreferredActivityHelper(mPm, mBroadcastHelper); - preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(), - changedUsers); - mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL); - }); - } } else if (!deletedPs.isSystem() && !outInfo.mIsUpdate && outInfo.mRemovedUsers != null && !deletedPs.isExternalStorage()) { // For non-system uninstalls with DELETE_KEEP_DATA, set the installed state to false @@ -394,6 +422,7 @@ final class RemovePackageHelper { deletedPs.setInstalled(/* installed= */ false, userId); } } + // make sure to preserve per-user installed state if this removal was just // a downgrade of a system app to the factory package boolean installedStateChanged = false; @@ -425,20 +454,6 @@ final class RemovePackageHelper { mPm.mSettings.writeKernelMappingLPr(deletedPs); } } - - if (removedAppId != -1) { - // A user ID was deleted here. Go through all users and remove it from KeyStore. - final int appIdToRemove = removedAppId; - mPm.mInjector.getBackgroundHandler().post(() -> { - try { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, - "clearKeystoreData:" + appIdToRemove); - mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, appIdToRemove); - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - }); - } } void cleanUpResources(File codeFile, String[] instructionSets) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index cfbaae3d0f30..cdf1f949a2d8 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -51,6 +51,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; +import android.content.pm.UserPackage; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.overlay.OverlayPaths; import android.net.Uri; @@ -1956,7 +1957,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile ArchiveState archiveState = null; int packageDepth = parser.getDepth(); - ArrayMap<String, SuspendParams> suspendParamsMap = null; + ArrayMap<UserPackage, SuspendParams> suspendParamsMap = null; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > packageDepth)) { @@ -1983,18 +1984,15 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile parser); break; case TAG_SUSPEND_PARAMS: - final String suspendingPackage = parser.getAttributeValue(null, - ATTR_SUSPENDING_PACKAGE); - if (suspendingPackage == null) { - Slog.wtf(TAG, "No suspendingPackage found inside tag " - + TAG_SUSPEND_PARAMS); + Map.Entry<UserPackage, SuspendParams> entry = + readSuspensionParamsLPr(userId, parser); + if (entry == null) { continue; } if (suspendParamsMap == null) { suspendParamsMap = new ArrayMap<>(); } - suspendParamsMap.put(suspendingPackage, - SuspendParams.restoreFromXml(parser)); + suspendParamsMap.put(entry.getKey(), entry.getValue()); break; case TAG_ARCHIVE_STATE: archiveState = parseArchiveState(parser); @@ -2016,7 +2014,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile oldSuspendedLauncherExtras, false /* quarantined */); suspendParamsMap = new ArrayMap<>(); - suspendParamsMap.put(oldSuspendingPackage, suspendParams); + suspendParamsMap.put( + UserPackage.of(userId, oldSuspendingPackage), suspendParams); } if (blockUninstall) { @@ -2058,6 +2057,20 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } } + @Nullable + private static Map.Entry<UserPackage, SuspendParams> readSuspensionParamsLPr( + int userId, TypedXmlPullParser parser) throws IOException { + final String suspendingPackage = parser.getAttributeValue(null, ATTR_SUSPENDING_PACKAGE); + if (suspendingPackage == null) { + Slog.wtf(TAG, "No suspendingPackage found inside tag " + TAG_SUSPEND_PARAMS); + return null; + } + final int suspendingUserId = userId; + return Map.entry( + UserPackage.of(suspendingUserId, suspendingPackage), + SuspendParams.restoreFromXml(parser)); + } + private static ArchiveState parseArchiveState(TypedXmlPullParser parser) throws XmlPullParserException, IOException { String installerTitle = parser.getAttributeValue(null, @@ -2414,10 +2427,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } if (ustate.isSuspended()) { for (int i = 0; i < ustate.getSuspendParams().size(); i++) { - final String suspendingPackage = ustate.getSuspendParams().keyAt(i); + final UserPackage suspendingPackage = + ustate.getSuspendParams().keyAt(i); serializer.startTag(null, TAG_SUSPEND_PARAMS); serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, - suspendingPackage); + suspendingPackage.packageName); final SuspendParams params = ustate.getSuspendParams().valueAt(i); if (params != null) { @@ -2556,7 +2570,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile outPs.getUsesSdkLibraries(), libName)); outPs.setUsesSdkLibrariesVersionsMajor(ArrayUtils.appendLong( outPs.getUsesSdkLibrariesVersionsMajor(), libVersion)); - outPs.setUsesSdkLibrariesOptional(PackageImpl.appendBoolean( + outPs.setUsesSdkLibrariesOptional(ArrayUtils.appendBoolean( outPs.getUsesSdkLibrariesOptional(), optional)); } diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index c2a960a95394..4e70cc52ef90 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -27,10 +27,10 @@ import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.content.Intent; import android.content.pm.SuspendDialogInfo; +import android.content.pm.UserPackage; import android.os.Binder; import android.os.Bundle; import android.os.PersistableBundle; -import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; @@ -88,8 +88,8 @@ public final class SuspendPackageHelper { * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that * should be shown to the user when they try to launch a suspended app. * Ignored if {@code suspended} is false. - * @param callingPackage The caller's package name. - * @param userId The user where packages reside. + * @param suspendingPackage The caller's package name. + * @param targetUserId The user where packages reside. * @param callingUid The caller's uid. * @return The names of failed packages. */ @@ -97,14 +97,14 @@ public final class SuspendPackageHelper { String[] setPackagesSuspended(@NonNull Computer snapshot, @Nullable String[] packageNames, boolean suspended, @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo, - @NonNull String callingPackage, @UserIdInt int userId, int callingUid, + @NonNull UserPackage suspendingPackage, @UserIdInt int targetUserId, int callingUid, boolean quarantined) { if (ArrayUtils.isEmpty(packageNames)) { return packageNames; } - if (suspended && !quarantined && !isSuspendAllowedForUser(snapshot, userId, - callingUid)) { - Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId); + if (suspended && !quarantined + && !isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) { + Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId); return packageNames; } @@ -119,19 +119,21 @@ public final class SuspendPackageHelper { final IntArray changedUids = new IntArray(packageNames.length); final boolean[] canSuspend = suspended - ? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid) + ? canSuspendPackageForUser(snapshot, packageNames, targetUserId, callingUid) : null; for (int i = 0; i < packageNames.length; i++) { final String packageName = packageNames[i]; - if (callingPackage.equals(packageName)) { - Slog.w(TAG, "Calling package: " + callingPackage + " trying to " + if (suspendingPackage.packageName.equals(packageName) + && suspendingPackage.userId == targetUserId) { + Slog.w(TAG, "Suspending package: " + suspendingPackage + " trying to " + (suspended ? "" : "un") + "suspend itself. Ignoring"); unmodifiablePackages.add(packageName); continue; } final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); - if (packageState == null || !packageState.getUserStateOrDefault(userId).isInstalled() - || snapshot.shouldFilterApplication(packageState, callingUid, userId)) { + if (packageState == null + || !packageState.getUserStateOrDefault(targetUserId).isInstalled() + || snapshot.shouldFilterApplication(packageState, callingUid, targetUserId)) { Slog.w(TAG, "Could not find package setting for package: " + packageName + ". Skipping suspending/un-suspending."); unmodifiablePackages.add(packageName); @@ -142,34 +144,34 @@ public final class SuspendPackageHelper { continue; } - final WatchedArrayMap<String, SuspendParams> suspendParamsMap = - packageState.getUserStateOrDefault(userId).getSuspendParams(); + final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap = + packageState.getUserStateOrDefault(targetUserId).getSuspendParams(); final SuspendParams oldSuspendParams = suspendParamsMap == null - ? null : suspendParamsMap.get(callingPackage); + ? null : suspendParamsMap.get(suspendingPackage); boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams); if (suspended && !changed) { // Carried over API behavior, must notify change even if no change notifyPackagesList.add(packageName); notifyUids.add( - UserHandle.getUid(userId, packageState.getAppId())); + UserHandle.getUid(targetUserId, packageState.getAppId())); continue; } - // If only the callingPackage is suspending this package, + // If only the suspendingPackage is suspending this package, // it will be unsuspended when this change is committed boolean packageUnsuspended = !suspended && CollectionUtils.size(suspendParamsMap) == 1 - && suspendParamsMap.containsKey(callingPackage); + && suspendParamsMap.containsKey(suspendingPackage); if (suspended || packageUnsuspended) { // Always notify of a suspend call + notify when fully unsuspended notifyPackagesList.add(packageName); - notifyUids.add(UserHandle.getUid(userId, packageState.getAppId())); + notifyUids.add(UserHandle.getUid(targetUserId, packageState.getAppId())); } if (changed) { changedPackagesList.add(packageName); - changedUids.add(UserHandle.getUid(userId, packageState.getAppId())); + changedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId())); } else { Slog.w(TAG, "No change is needed for package: " + packageName + ". Skipping suspending/un-suspending."); @@ -181,11 +183,11 @@ public final class SuspendPackageHelper { for (int index = 0; index < size; index++) { final String packageName = changedPackagesList.valueAt(index); final PackageUserStateWrite userState = mutator.forPackage(packageName) - .userState(userId); + .userState(targetUserId); if (suspended) { - userState.putSuspendParams(callingPackage, newSuspendParams); + userState.putSuspendParams(suspendingPackage, newSuspendParams); } else { - userState.removeSuspension(callingPackage); + userState.removeSuspension(suspendingPackage); } } }); @@ -197,17 +199,17 @@ public final class SuspendPackageHelper { mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, suspended ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED, - changedPackages, notifyUids.toArray(), quarantined, userId); + changedPackages, notifyUids.toArray(), quarantined, targetUserId); mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages, - suspended, userId); - mPm.scheduleWritePackageRestrictions(userId); + suspended, targetUserId); + mPm.scheduleWritePackageRestrictions(targetUserId); } // Send the suspension changed broadcast to ensure suspension state is not stale. if (!changedPackagesList.isEmpty()) { mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined, - userId); + targetUserId); } return unmodifiablePackages.toArray(new String[0]); } @@ -216,19 +218,19 @@ public final class SuspendPackageHelper { * Returns the names in the {@code packageNames} which can not be suspended by the caller. * * @param packageNames The names of packages to check. - * @param userId The user where packages reside. + * @param targetUserId The user where packages reside. * @param callingUid The caller's uid. * @return The names of packages which are Unsuspendable. */ @NonNull String[] getUnsuspendablePackagesForUser(@NonNull Computer snapshot, - @NonNull String[] packageNames, @UserIdInt int userId, int callingUid) { - if (!isSuspendAllowedForUser(snapshot, userId, callingUid)) { - Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId); + @NonNull String[] packageNames, @UserIdInt int targetUserId, int callingUid) { + if (!isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) { + Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId); return packageNames; } final ArraySet<String> unactionablePackages = new ArraySet<>(); - final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, userId, + final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, targetUserId, callingUid); for (int i = 0; i < packageNames.length; i++) { if (!canSuspend[i]) { @@ -237,7 +239,7 @@ public final class SuspendPackageHelper { } final PackageStateInternal packageState = snapshot.getPackageStateForInstalledAndFiltered( - packageNames[i], callingUid, userId); + packageNames[i], callingUid, targetUserId); if (packageState == null) { Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]); unactionablePackages.add(packageNames[i]); @@ -285,30 +287,31 @@ public final class SuspendPackageHelper { * @param packagesToChange The packages on which the suspension are to be removed. * @param suspendingPackagePredicate A predicate identifying the suspending packages whose * suspensions will be removed. - * @param userId The user for which the changes are taking place. + * @param targetUserId The user for which the changes are taking place. */ void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot, @NonNull String[] packagesToChange, - @NonNull Predicate<String> suspendingPackagePredicate, int userId) { + @NonNull Predicate<UserPackage> suspendingPackagePredicate, int targetUserId) { final List<String> unsuspendedPackages = new ArrayList<>(); final IntArray unsuspendedUids = new IntArray(); - final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>(); + final ArrayMap<String, ArraySet<UserPackage>> pkgToSuspendingPkgsToCommit = + new ArrayMap<>(); for (String packageName : packagesToChange) { final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); final PackageUserStateInternal packageUserState = packageState == null - ? null : packageState.getUserStateOrDefault(userId); + ? null : packageState.getUserStateOrDefault(targetUserId); if (packageUserState == null || !packageUserState.isSuspended()) { continue; } - WatchedArrayMap<String, SuspendParams> suspendParamsMap = + WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap = packageUserState.getSuspendParams(); int countRemoved = 0; for (int index = 0; index < suspendParamsMap.size(); index++) { - String suspendingPackage = suspendParamsMap.keyAt(index); + UserPackage suspendingPackage = suspendParamsMap.keyAt(index); if (suspendingPackagePredicate.test(suspendingPackage)) { - ArraySet<String> suspendingPkgsToCommit = + ArraySet<UserPackage> suspendingPkgsToCommit = pkgToSuspendingPkgsToCommit.get(packageName); if (suspendingPkgsToCommit == null) { suspendingPkgsToCommit = new ArraySet<>(); @@ -322,31 +325,33 @@ public final class SuspendPackageHelper { // Everything would be removed and package unsuspended if (countRemoved == suspendParamsMap.size()) { unsuspendedPackages.add(packageState.getPackageName()); - unsuspendedUids.add(UserHandle.getUid(userId, packageState.getAppId())); + unsuspendedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId())); } } mPm.commitPackageStateMutation(null, mutator -> { for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) { String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex); - ArraySet<String> packagesToRemove = pkgToSuspendingPkgsToCommit.valueAt(mapIndex); - PackageUserStateWrite userState = mutator.forPackage(packageName).userState(userId); + ArraySet<UserPackage> packagesToRemove = + pkgToSuspendingPkgsToCommit.valueAt(mapIndex); + PackageUserStateWrite userState = + mutator.forPackage(packageName).userState(targetUserId); for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) { userState.removeSuspension(packagesToRemove.valueAt(setIndex)); } } }); - mPm.scheduleWritePackageRestrictions(userId); + mPm.scheduleWritePackageRestrictions(targetUserId); final Computer newSnapshot = mPm.snapshotComputer(); if (!unsuspendedPackages.isEmpty()) { final String[] packageArray = unsuspendedPackages.toArray( new String[unsuspendedPackages.size()]); mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray, - false, userId); + false, targetUserId); mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot, Intent.ACTION_PACKAGES_UNSUSPENDED, - packageArray, unsuspendedUids.toArray(), false, userId); + packageArray, unsuspendedUids.toArray(), false, targetUserId); } } @@ -404,7 +409,7 @@ public final class SuspendPackageHelper { * @return The name of suspending package. */ @Nullable - String getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage, + UserPackage getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage, int userId, int callingUid) { final PackageStateInternal packageState = snapshot.getPackageStateInternal( suspendedPackage, callingUid); @@ -417,13 +422,13 @@ public final class SuspendPackageHelper { return null; } - String suspendingPackage = null; - String suspendedBySystem = null; - String qasPackage = null; + UserPackage suspendingPackage = null; + UserPackage suspendedBySystem = null; + UserPackage qasPackage = null; for (int i = 0; i < userState.getSuspendParams().size(); i++) { suspendingPackage = userState.getSuspendParams().keyAt(i); var suspendParams = userState.getSuspendParams().valueAt(i); - if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { + if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) { suspendedBySystem = suspendingPackage; } if (suspendParams.isQuarantined() && qasPackage == null) { @@ -451,7 +456,7 @@ public final class SuspendPackageHelper { */ @Nullable SuspendDialogInfo getSuspendedDialogInfo(@NonNull Computer snapshot, - @NonNull String suspendedPackage, @NonNull String suspendingPackage, int userId, + @NonNull String suspendedPackage, @NonNull UserPackage suspendingPackage, int userId, int callingUid) { final PackageStateInternal packageState = snapshot.getPackageStateInternal( suspendedPackage, callingUid); @@ -464,7 +469,7 @@ public final class SuspendPackageHelper { return null; } - final WatchedArrayMap<String, SuspendParams> suspendParamsMap = + final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap = userState.getSuspendParams(); if (suspendParamsMap == null) { return null; @@ -493,34 +498,36 @@ public final class SuspendPackageHelper { * be suspended or not. * * @param packageNames The package names to check suspendability for. - * @param userId The user to check in + * @param targetUserId The user to check in * @param callingUid The caller's uid. * @return An array containing results of the checks */ @NonNull boolean[] canSuspendPackageForUser(@NonNull Computer snapshot, @NonNull String[] packageNames, - int userId, int callingUid) { + int targetUserId, int callingUid) { final boolean[] canSuspend = new boolean[packageNames.length]; - final boolean isCallerOwner = isCallerDeviceOrProfileOwner(snapshot, userId, callingUid); + final boolean isCallerOwner = + isCallerDeviceOrProfileOwner(snapshot, targetUserId, callingUid); final long token = Binder.clearCallingIdentity(); try { final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider(); - final String activeLauncherPackageName = defaultAppProvider.getDefaultHome(userId); - final String dialerPackageName = defaultAppProvider.getDefaultDialer(userId); + final String activeLauncherPackageName = + defaultAppProvider.getDefaultHome(targetUserId); + final String dialerPackageName = defaultAppProvider.getDefaultDialer(targetUserId); final String requiredInstallerPackage = - getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, userId); + getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, targetUserId); final String requiredUninstallerPackage = - getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, userId); + getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, targetUserId); final String requiredVerifierPackage = - getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, userId); + getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, targetUserId); final String requiredPermissionControllerPackage = getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER, - userId); + targetUserId); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; final String packageName = packageNames[i]; - if (mPm.isPackageDeviceAdmin(packageName, userId)) { + if (mPm.isPackageDeviceAdmin(packageName, targetUserId)) { Slog.w(TAG, "Cannot suspend package \"" + packageName + "\": has an active device admin"); continue; @@ -555,12 +562,12 @@ public final class SuspendPackageHelper { + "\": required for permissions management"); continue; } - if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { + if (mProtectedPackages.isPackageStateProtected(targetUserId, packageName)) { Slog.w(TAG, "Cannot suspend package \"" + packageName + "\": protected package"); continue; } - if (!isCallerOwner && snapshot.getBlockUninstall(userId, packageName)) { + if (!isCallerOwner && snapshot.getBlockUninstall(targetUserId, packageName)) { Slog.w(TAG, "Cannot suspend package \"" + packageName + "\": blocked by admin"); continue; @@ -572,7 +579,7 @@ public final class SuspendPackageHelper { PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); AndroidPackage pkg = packageState == null ? null : packageState.getPkg(); if (pkg != null) { - final int uid = UserHandle.getUid(userId, packageState.getAppId()); + final int uid = UserHandle.getUid(targetUserId, packageState.getAppId()); // Cannot suspend SDK libs as they are controlled by SDK manager. if (pkg.isSdkLibrary()) { Slog.w(TAG, "Cannot suspend package: " + packageName @@ -614,20 +621,6 @@ public final class SuspendPackageHelper { == AppOpsManager.MODE_ALLOWED; } - /** - * Suspends packages on behalf of an admin. - * - * @return array of packages that are unsuspendable, either because admin is not allowed to - * suspend them (e.g. current dialer) or there was other problem (e.g. package not found). - */ - public String[] setPackagesSuspendedByAdmin( - Computer snapshot, int userId, String[] packageNames, boolean suspend) { - return setPackagesSuspended(snapshot, packageNames, suspend, - null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, - PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID, - false /* quarantined */); - } - private String getKnownPackageName(@NonNull Computer snapshot, @KnownPackages.KnownPackage int knownPackage, int userId) { final String[] knownPackages = @@ -635,14 +628,15 @@ public final class SuspendPackageHelper { return knownPackages.length > 0 ? knownPackages[0] : null; } - private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int userId, + private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int targetUserId, int callingUid) { if (callingUid == SYSTEM_UID) { return true; } - final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId); + final String ownerPackage = + mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId); if (ownerPackage != null) { - return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, userId, + return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, targetUserId, callingUid); } return false; diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index 7386301bdd6e..14db70e5f72e 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -306,7 +306,6 @@ public final class UserTypeFactory { .setDarkThemeBadgeColors( R.color.white) .setDefaultRestrictions(getDefaultProfileRestrictions()) - .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings()) .setDefaultUserProperties(new UserProperties.Builder() .setStartWithParent(true) .setCredentialShareableWithParent(true) diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index fa54f0ba18cd..d0fe9647618a 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -28,6 +28,7 @@ import android.content.pm.ConfigurationInfo; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; +import android.content.pm.Flags; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; @@ -474,7 +475,34 @@ public class PackageInfoUtils { } info.sharedLibraryFiles = usesLibraryFiles.isEmpty() ? null : usesLibraryFiles.toArray(new String[0]); - info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos; + + + if (!Flags.sdkLibIndependence()) { + info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos; + info.optionalSharedLibraryInfos = null; + } else { + // sharedLibraryInfos contains all shared libraries that the app depends on (including + // the optional sdk libraries) + info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos; + String[] libsNames = pkgSetting.getUsesSdkLibraries(); + boolean[] libsOptional = pkgSetting.getUsesSdkLibrariesOptional(); + List<SharedLibraryInfo> optionalSdkLibraries = null; + if (!ArrayUtils.isEmpty(libsOptional) && !ArrayUtils.isEmpty(libsNames) + && libsNames.length == libsOptional.length) { + for (SharedLibraryInfo info1 : usesLibraryInfos) { + if (info1.getType() == SharedLibraryInfo.TYPE_SDK_PACKAGE) { + int index = ArrayUtils.indexOf(libsNames, info1.getName()); + if (index >= 0 && libsOptional[index]) { + if (optionalSdkLibraries == null) { + optionalSdkLibraries = new ArrayList<>(); + } + optionalSdkLibraries.add(info1); + } + } + } + } + info.optionalSharedLibraryInfos = optionalSdkLibraries; + } if (info.category == ApplicationInfo.CATEGORY_UNDEFINED) { info.category = pkgSetting.getCategoryOverride(); } diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java index 2f4ad2d8fcc6..15b693cf72f8 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.PackageManager; +import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.util.ArraySet; import android.util.Pair; @@ -173,7 +174,7 @@ class PackageUserStateDefault implements PackageUserStateInternal { @Nullable @Override - public WatchedArrayMap<String, SuspendParams> getSuspendParams() { + public WatchedArrayMap<UserPackage, SuspendParams> getSuspendParams() { return null; } diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java index c5ef5257ddd5..7a5a14d8d3c2 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.PackageManager; +import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.text.TextUtils; import android.util.ArrayMap; @@ -121,7 +122,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt * Suspending package to suspend params */ @Nullable - private WatchedArrayMap<String, SuspendParams> mSuspendParams; + private WatchedArrayMap<UserPackage, SuspendParams> mSuspendParams; @Nullable private WatchedArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap; @@ -369,7 +370,10 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt return !CollectionUtils.isEmpty(mSuspendParams); } - public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage, + /** + * Adds or updates suspension params by the given package. + */ + public PackageUserStateImpl putSuspendParams(@NonNull UserPackage suspendingPackage, @Nullable SuspendParams suspendParams) { if (mSuspendParams == null) { mSuspendParams = new WatchedArrayMap<>(); @@ -384,7 +388,10 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt return this; } - public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) { + /** + * Removes suspension by the given package. + */ + public PackageUserStateImpl removeSuspension(@NonNull UserPackage suspendingPackage) { if (mSuspendParams != null) { mSuspendParams.remove(suspendingPackage); onChanged(); @@ -565,7 +572,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt * Suspending package to suspend params */ public @NonNull PackageUserStateImpl setSuspendParams( - @NonNull ArrayMap<String, SuspendParams> value) { + @NonNull ArrayMap<UserPackage, SuspendParams> value) { if (value == null) { return this; } @@ -778,7 +785,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt * Suspending package to suspend params */ @DataClass.Generated.Member - public @Nullable WatchedArrayMap<String,SuspendParams> getSuspendParams() { + public @Nullable WatchedArrayMap<UserPackage,SuspendParams> getSuspendParams() { return mSuspendParams; } @@ -830,7 +837,7 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt * Suspending package to suspend params */ @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setSuspendParams(@NonNull WatchedArrayMap<String,SuspendParams> value) { + public @NonNull PackageUserStateImpl setSuspendParams(@NonNull WatchedArrayMap<UserPackage,SuspendParams> value) { mSuspendParams = value; return this; } @@ -909,10 +916,10 @@ public class PackageUserStateImpl extends WatchableImpl implements PackageUserSt } @DataClass.Generated( - time = 1701470095849L, + time = 1701864813354L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java", - inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate long mDeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)") + inputSignatures = "private int mBooleans\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate long mDeDataInode\nprivate int mDistractionFlags\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate @android.annotation.Nullable com.android.server.pm.pkg.ArchiveState mArchiveState\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(android.content.pm.UserPackage)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setArchiveState(com.android.server.pm.pkg.ArchiveState)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isQuarantined()\npublic @java.lang.Override boolean dataExists()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\nprivate static final int INSTALLED\nprivate static final int STOPPED\nprivate static final int NOT_LAUNCHED\nprivate static final int HIDDEN\nprivate static final int INSTANT_APP\nprivate static final int VIRTUAL_PRELOADED\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java index 46cc830130ef..f8d745cb7fbf 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java @@ -19,6 +19,7 @@ package com.android.server.pm.pkg; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; +import android.content.pm.UserPackage; import android.content.pm.pkg.FrameworkPackageUserState; import android.util.Pair; @@ -38,7 +39,7 @@ public interface PackageUserStateInternal extends PackageUserState, FrameworkPac // TODO: Make non-null with emptyMap() @Nullable - WatchedArrayMap<String, SuspendParams> getSuspendParams(); + WatchedArrayMap<UserPackage, SuspendParams> getSuspendParams(); @Nullable WatchedArraySet<String> getDisabledComponentsNoCopy(); diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java index 8430cf7a0d11..253eb4006122 100644 --- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java +++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.util.ArraySet; @@ -349,7 +350,7 @@ public class PackageStateMutator { @NonNull @Override - public PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage, + public PackageUserStateWrite putSuspendParams(@NonNull UserPackage suspendingPackage, @Nullable SuspendParams suspendParams) { if (mUserState != null) { mUserState.putSuspendParams(suspendingPackage, suspendParams); @@ -359,7 +360,7 @@ public class PackageStateMutator { @NonNull @Override - public PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage) { + public PackageUserStateWrite removeSuspension(@NonNull UserPackage suspendingPackage) { if (mUserState != null) { mUserState.removeSuspension(suspendingPackage); } diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java index 0c6c6723b79b..f6b21045a8bb 100644 --- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java +++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.PackageManager; +import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import com.android.server.pm.pkg.PackageUserStateImpl; @@ -38,11 +39,11 @@ public interface PackageUserStateWrite { @PackageManager.DistractionRestriction int restrictionFlags); @NonNull - PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage, + PackageUserStateWrite putSuspendParams(@NonNull UserPackage suspendingPackage, @Nullable SuspendParams suspendParams); @NonNull - PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage); + PackageUserStateWrite removeSuspension(@NonNull UserPackage suspendingPackage); @NonNull PackageUserStateWrite setHidden(boolean hidden); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 6f2750767094..d903ad4d9f0d 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -41,7 +41,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; -import android.content.res.Resources; import android.graphics.Rect; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; @@ -2081,6 +2080,45 @@ public final class TvInputManagerService extends SystemService { } @Override + public void stopPlayback(IBinder sessionToken, int mode, int userId) { + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "stopPlayback"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + getSessionLocked(sessionToken, callingUid, resolvedUserId).stopPlayback( + mode); + } catch (RemoteException | SessionNotFoundException e) { + Slog.e(TAG, "error in stopPlayback(mode)", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void startPlayback(IBinder sessionToken, int userId) { + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "stopPlayback"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + getSessionLocked(sessionToken, callingUid, resolvedUserId).startPlayback(); + } catch (RemoteException | SessionNotFoundException e) { + Slog.e(TAG, "error in startPlayback()", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) { final int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 315e7d85df24..4b55bec928c7 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -18,17 +18,17 @@ package com.android.server.wm; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; import static android.app.Activity.FULLSCREEN_MODE_REQUEST_ENTER; +import static android.app.Activity.FULLSCREEN_MODE_REQUEST_EXIT; import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.INVALID_WINDOWING_MODE; import static android.app.FullscreenRequestHandler.REMOTE_CALLBACK_RESULT_KEY; import static android.app.FullscreenRequestHandler.RESULT_APPROVED; -import static android.app.FullscreenRequestHandler.RESULT_FAILED_NOT_DEFAULT_FREEFORM; -import static android.app.FullscreenRequestHandler.RESULT_FAILED_NOT_IN_FREEFORM; import static android.app.FullscreenRequestHandler.RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY; import static android.app.FullscreenRequestHandler.RESULT_FAILED_NOT_TOP_FOCUSED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; @@ -1092,19 +1092,14 @@ class ActivityClientController extends IActivityClientController.Stub { private @FullscreenRequestHandler.RequestResult int validateMultiwindowFullscreenRequestLocked( Task topFocusedRootTask, int fullscreenRequest, ActivityRecord requesterActivity) { - // If the mode is not by default freeform, the freeform will be a user-driven event. - if (topFocusedRootTask.getParent().getWindowingMode() != WINDOWING_MODE_FREEFORM) { - return RESULT_FAILED_NOT_DEFAULT_FREEFORM; + if (requesterActivity.getWindowingMode() == WINDOWING_MODE_PINNED) { + return RESULT_APPROVED; } // If this is not coming from the currently top-most activity, reject the request. if (requesterActivity != topFocusedRootTask.getTopMostActivity()) { return RESULT_FAILED_NOT_TOP_FOCUSED; } - if (fullscreenRequest == FULLSCREEN_MODE_REQUEST_ENTER) { - if (topFocusedRootTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) { - return RESULT_FAILED_NOT_IN_FREEFORM; - } - } else { + if (fullscreenRequest == FULLSCREEN_MODE_REQUEST_EXIT) { if (topFocusedRootTask.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { return RESULT_FAILED_NOT_IN_FULLSCREEN_WITH_HISTORY; } diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index f9d344bd7e31..1b45c1b4f3f1 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -48,6 +48,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; +import android.content.pm.UserPackage; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -335,19 +336,19 @@ class ActivityStartInterceptor { return false; } final String suspendedPackage = mAInfo.applicationInfo.packageName; - final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId); - if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { + final UserPackage suspender = pmi.getSuspendingPackage(suspendedPackage, mUserId); + if (suspender != null && PLATFORM_PACKAGE_NAME.equals(suspender.packageName)) { return interceptSuspendedByAdminPackage(); } final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, - suspendingPackage, mUserId); + suspender, mUserId); final Bundle crossProfileOptions = hasCrossProfileAnimation() ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle() : null; final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, FLAG_IMMUTABLE); mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage, - suspendingPackage, dialogInfo, crossProfileOptions, target, mUserId); + suspender, dialogInfo, crossProfileOptions, target, mUserId); mCallingPid = mRealCallingPid; mCallingUid = mRealCallingUid; mResolvedType = null; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 4b0177a36ebe..630b9e139456 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -84,7 +84,9 @@ import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENS import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK; import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST; import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -140,6 +142,8 @@ import com.android.server.wm.LaunchParamsController.LaunchParams; import com.android.server.wm.TaskFragment.EmbeddingCheckResult; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.text.DateFormat; import java.util.Date; @@ -230,7 +234,26 @@ class ActivityStarter { private boolean mIsTaskCleared; private boolean mMovedToFront; private boolean mNoAnimation; - private boolean mAvoidMoveToFront; + + // TODO mAvoidMoveToFront before V is changed from a boolean to a int code mCanMoveToFrontCode + // for the purpose of attribution of new BAL V feature. This should be reverted back to the + // boolean flag post V. + @IntDef(prefix = {"MOVE_TO_FRONT_"}, value = { + MOVE_TO_FRONT_ALLOWED, + MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS, + MOVE_TO_FRONT_AVOID_LEGACY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MoveToFrontCode {} + + // Allows a task move to front. + private static final int MOVE_TO_FRONT_ALLOWED = 0; + // Avoid a task move to front because the Pending Intent that starts the activity only + // its creator has the BAL privilege, its sender does not. + private static final int MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS = 1; + // Avoid a task move to front because of all other legacy reasons. + private static final int MOVE_TO_FRONT_AVOID_LEGACY = 2; + private @MoveToFrontCode int mCanMoveToFrontCode = MOVE_TO_FRONT_ALLOWED; private boolean mFrozeTaskList; private boolean mTransientLaunch; // The task which was above the targetTask before starting this activity. null if the targetTask @@ -642,7 +665,7 @@ class ActivityStarter { mIsTaskCleared = starter.mIsTaskCleared; mMovedToFront = starter.mMovedToFront; mNoAnimation = starter.mNoAnimation; - mAvoidMoveToFront = starter.mAvoidMoveToFront; + mCanMoveToFrontCode = starter.mCanMoveToFrontCode; mFrozeTaskList = starter.mFrozeTaskList; mVoiceSession = starter.mVoiceSession; @@ -1499,6 +1522,14 @@ class ActivityStarter { return result; } + private boolean avoidMoveToFront() { + return mCanMoveToFrontCode != MOVE_TO_FRONT_ALLOWED; + } + + private boolean avoidMoveToFrontPIOnlyCreatorAllows() { + return mCanMoveToFrontCode == MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS; + } + /** * If the start result is success, ensure that the configuration of the started activity matches * the current display. Otherwise clean up unassociated containers to avoid leakage. @@ -1552,7 +1583,7 @@ class ActivityStarter { currentTop, currentTop.getDisplayId(), false /* deferResume */); } - if (!mAvoidMoveToFront && mDoResume && mRootWindowContainer + if (!avoidMoveToFront() && mDoResume && mRootWindowContainer .hasVisibleWindowAboveButDoesNotOwnNotificationShade(started.launchedFromUid)) { // If the UID launching the activity has a visible window on top of the notification // shade and it's launching an activity that's going to be at the front, we should move @@ -1689,10 +1720,18 @@ class ActivityStarter { } // When running transient transition, the transient launch target should keep on top. // So disallow the transient hide activity to move itself to front, e.g. trampoline. - if (!mAvoidMoveToFront && (mService.mHomeProcess == null + if (!avoidMoveToFront() && (mService.mHomeProcess == null || mService.mHomeProcess.mUid != realCallingUid) && r.mTransitionController.isTransientHide(targetTask)) { - mAvoidMoveToFront = true; + mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY; + } + // If the activity is started by sending a pending intent and only its creator has the + // privilege to allow BAL (its sender does not), avoid move it to the front. Only do + // this when it is not a new task and not already been marked as avoid move to front. + // Guarded by a flag: balDontBringExistingBackgroundTaskStackToFg + if (balDontBringExistingBackgroundTaskStackToFg() && !avoidMoveToFront() + && balVerdict.onlyCreatorAllows()) { + mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_PI_ONLY_CREATOR_ALLOWS; } mPriorAboveTask = TaskDisplayArea.getRootTaskAbove(targetTask.getRootTask()); } @@ -1746,15 +1785,19 @@ class ActivityStarter { // After activity is attached to task, but before actual start recordTransientLaunchIfNeeded(mLastStartActivityRecord); - if (!mAvoidMoveToFront && mDoResume) { - logOnlyCreatorAllowsBAL(balVerdict, realCallingUid, newTask); - mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask); - if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.isDreaming() - && !dreamStopping) { - // Launching underneath dream activity (fullscreen, always-on-top). Run the launch- - // -behind transition so the Activity gets created and starts in visible state. - mLaunchTaskBehind = true; - r.mLaunchTaskBehind = true; + if (mDoResume) { + if (!avoidMoveToFront()) { + mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask); + if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.isDreaming() + && !dreamStopping) { + // Launching underneath dream activity (fullscreen, always-on-top). Run the + // launch--behind transition so the Activity gets created and starts + // in visible state. + mLaunchTaskBehind = true; + r.mLaunchTaskBehind = true; + } + } else { + logPIOnlyCreatorAllowsBAL(); } } @@ -1816,10 +1859,13 @@ class ActivityStarter { // root-task to the will not update the focused root-task. If starting the new // activity now allows the task root-task to be focusable, then ensure that we // now update the focused root-task accordingly. - if (!mAvoidMoveToFront && mTargetRootTask.isTopActivityFocusable() + if (mTargetRootTask.isTopActivityFocusable() && !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) { - logOnlyCreatorAllowsBAL(balVerdict, realCallingUid, newTask); - mTargetRootTask.moveToFront("startActivityInner"); + if (!avoidMoveToFront()) { + mTargetRootTask.moveToFront("startActivityInner"); + } else { + logPIOnlyCreatorAllowsBAL(); + } } mRootWindowContainer.resumeFocusedTasksTopActivities( mTargetRootTask, mStartActivity, mOptions, mTransientLaunch); @@ -1847,25 +1893,24 @@ class ActivityStarter { return START_SUCCESS; } - private void logOnlyCreatorAllowsBAL(BalVerdict balVerdict, - int realCallingUid, boolean newTask) { - // TODO (b/296478675) eventually, we will prevent such case from happening - // and probably also log that a BAL is prevented by android V. - if (!newTask && balVerdict.onlyCreatorAllows()) { - String realCallingPackage = - mService.mContext.getPackageManager().getNameForUid(realCallingUid); - if (realCallingPackage == null) { - realCallingPackage = "uid=" + realCallingUid; - } - Slog.wtf(TAG, "A background app is brought to the foreground due to a " - + "PendingIntent. However, only the creator of the PendingIntent allows BAL, " - + "while the sender does not allow BAL. realCallingPackage: " - + realCallingPackage + "; callingPackage: " + mRequest.callingPackage - + "; mTargetRootTask:" + mTargetRootTask + "; mIntent: " + mIntent - + "; mTargetRootTask.getTopNonFinishingActivity: " - + mTargetRootTask.getTopNonFinishingActivity() - + "; mTargetRootTask.getRootActivity: " + mTargetRootTask.getRootActivity()); + // TODO (b/316135632) Post V release, remove this log method. + private void logPIOnlyCreatorAllowsBAL() { + if (!avoidMoveToFrontPIOnlyCreatorAllows()) return; + String realCallingPackage = + mService.mContext.getPackageManager().getNameForUid(mRealCallingUid); + if (realCallingPackage == null) { + realCallingPackage = "uid=" + mRealCallingUid; } + Slog.wtf(TAG, "Without Android 15 BAL hardening this activity would be moved to the " + + "foreground. The activity is started by a PendingIntent. However, only the " + + "creator of the PendingIntent allows BAL while the sender does not allow BAL. " + + "realCallingPackage: " + realCallingPackage + + "; callingPackage: " + mRequest.callingPackage + + "; mTargetRootTask:" + mTargetRootTask + + "; mIntent: " + mIntent + + "; mTargetRootTask.getTopNonFinishingActivity: " + + mTargetRootTask.getTopNonFinishingActivity() + + "; mTargetRootTask.getRootActivity: " + mTargetRootTask.getRootActivity()); } private void recordTransientLaunchIfNeeded(ActivityRecord r) { @@ -2064,7 +2109,7 @@ class ActivityStarter { mRootWindowContainer.startPowerModeLaunchIfNeeded(false /* forceSend */, targetTaskTop); - setTargetRootTaskIfNeeded(targetTaskTop, balVerdict); + setTargetRootTaskIfNeeded(targetTaskTop); // When there is a reused activity and the current result is a trampoline activity, // set the reused activity as the result. @@ -2080,13 +2125,12 @@ class ActivityStarter { if (!mMovedToFront && mDoResume) { ProtoLog.d(WM_DEBUG_TASKS, "Bring to front target: %s from %s", mTargetRootTask, targetTaskTop); - logOnlyCreatorAllowsBAL(balVerdict, mRealCallingUid, false); mTargetRootTask.moveToFront("intentActivityFound"); } + resumeTargetRootTaskIfNeeded(); return START_RETURN_INTENT_TO_CALLER; } - complyActivityFlags(targetTask, reusedTask != null ? reusedTask.getTopNonFinishingActivity() : null, intentGrants); @@ -2109,7 +2153,6 @@ class ActivityStarter { targetTaskTop.showStartingWindow(true /* taskSwitch */); } else if (mDoResume) { // Make sure the root task and its belonging display are moved to topmost. - logOnlyCreatorAllowsBAL(balVerdict, mRealCallingUid, false); mTargetRootTask.moveToFront("intentActivityFound"); } // We didn't do anything... but it was needed (a.k.a., client don't use that intent!) @@ -2344,7 +2387,7 @@ class ActivityStarter { mIsTaskCleared = false; mMovedToFront = false; mNoAnimation = false; - mAvoidMoveToFront = false; + mCanMoveToFrontCode = MOVE_TO_FRONT_ALLOWED; mFrozeTaskList = false; mTransientLaunch = false; mPriorAboveTask = null; @@ -2456,12 +2499,12 @@ class ActivityStarter { // The caller specifies that we'd like to be avoided to be moved to the // front, so be it! mDoResume = false; - mAvoidMoveToFront = true; + mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY; } } } else if (mOptions.getAvoidMoveToFront()) { mDoResume = false; - mAvoidMoveToFront = true; + mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY; } mTransientLaunch = mOptions.getTransientLaunch(); final KeyguardController kc = mSupervisor.getKeyguardController(); @@ -2471,7 +2514,7 @@ class ActivityStarter { if (mTransientLaunch && mDisplayLockAndOccluded && mService.getTransitionController().isShellTransitionsEnabled()) { mDoResume = false; - mAvoidMoveToFront = true; + mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY; } mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask()); @@ -2528,7 +2571,7 @@ class ActivityStarter { mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0; if (mBalCode == BAL_BLOCK && !mService.isBackgroundActivityStartsEnabled()) { - mAvoidMoveToFront = true; + mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY; mDoResume = false; } } @@ -2705,7 +2748,7 @@ class ActivityStarter { * @param intentActivity Existing matching activity. * @return {@link ActivityRecord} brought to front. */ - private void setTargetRootTaskIfNeeded(ActivityRecord intentActivity, BalVerdict balVerdict) { + private void setTargetRootTaskIfNeeded(ActivityRecord intentActivity) { intentActivity.getTaskFragment().clearLastPausedActivity(); Task intentTask = intentActivity.getTask(); // The intent task might be reparented while in getOrCreateRootTask, caches the original @@ -2742,7 +2785,7 @@ class ActivityStarter { differentTopTask = true; } - if (differentTopTask && !mAvoidMoveToFront) { + if (differentTopTask && !avoidMoveToFront()) { mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); if (mSourceRecord == null || inTopNonFinishingTask(mSourceRecord)) { // We really do want to push this one into the user's face, right now. @@ -2772,7 +2815,6 @@ class ActivityStarter { // task on top there. // Defer resuming the top activity while moving task to top, since the // current task-top activity may not be the activity that should be resumed. - logOnlyCreatorAllowsBAL(balVerdict, mRealCallingUid, false); mTargetRootTask.moveTaskToFront(intentTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, DEFER_RESUME, "bringingFoundTaskToFront"); @@ -2789,7 +2831,9 @@ class ActivityStarter { mOptions = null; } } - + if (differentTopTask) { + logPIOnlyCreatorAllowsBAL(); + } // Update the target's launch cookie and pending remote animation to those specified in the // options if set. if (mStartActivity.mLaunchCookie != null) { @@ -2840,7 +2884,7 @@ class ActivityStarter { } private void setNewTask(Task taskToAffiliate) { - final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront; + final boolean toTop = !mLaunchTaskBehind && !avoidMoveToFront(); final Task task = mTargetRootTask.reuseOrCreateTask( mStartActivity.info, mIntent, mVoiceSession, mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java index 0d15fc932e68..2b841fdad9c0 100644 --- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java +++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH; import static com.android.internal.R.bool.config_unfoldTransitionEnabled; import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY; @@ -110,15 +111,6 @@ public class PhysicalDisplaySwitchTransitionLauncher { return; } - final TransitionRequestInfo.DisplayChange displayChange = - new TransitionRequestInfo.DisplayChange(displayId); - - final Rect startAbsBounds = new Rect(0, 0, oldDisplayWidth, oldDisplayHeight); - displayChange.setStartAbsBounds(startAbsBounds); - final Rect endAbsBounds = new Rect(0, 0, newDisplayWidth, newDisplayHeight); - displayChange.setEndAbsBounds(endAbsBounds); - displayChange.setPhysicalDisplayChanged(true); - mTransition = null; if (mTransitionController.isCollecting()) { @@ -128,10 +120,20 @@ public class PhysicalDisplaySwitchTransitionLauncher { // Make sure that transition is not ready until we finish the remote display change mTransition.setReady(mDisplayContent, false); + mTransition.addFlag(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH); ProtoLog.d(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Adding display switch to existing collecting transition"); } else { + final TransitionRequestInfo.DisplayChange displayChange = + new TransitionRequestInfo.DisplayChange(displayId); + + final Rect startAbsBounds = new Rect(0, 0, oldDisplayWidth, oldDisplayHeight); + displayChange.setStartAbsBounds(startAbsBounds); + final Rect endAbsBounds = new Rect(0, 0, newDisplayWidth, newDisplayHeight); + displayChange.setEndAbsBounds(endAbsBounds); + displayChange.setPhysicalDisplayChanged(true); + mTransition = mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE, 0 /* flags */, mDisplayContent, mDisplayContent, null /* remoteTransition */, diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index d66b9b956071..2a0f1e2ede55 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -468,7 +468,6 @@ private: borrowed_fd incomingFd, bool waitOnEof, std::vector<char>* buffer, std::vector<IncFsDataBlock>* blocks) { IncFsSize remaining = size; - IncFsSize totalSize = 0; IncFsBlockIndex blockIdx = 0; while (remaining > 0) { constexpr auto capacity = BUFFER_SIZE; @@ -502,7 +501,6 @@ private: buffer->resize(size + read); remaining -= read; - totalSize += read; } if (!buffer->empty() && !flashToIncFs(incfsFd, kind, true, &blockIdx, buffer, blocks)) { return false; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e0a2f30b1831..a490013303e9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2469,7 +2469,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void migratePersonalAppSuspensionLocked( int doUserId, int poUserId, ActiveAdmin poAdmin) { final PackageManagerInternal pmi = mInjector.getPackageManagerInternal(); - if (!pmi.isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, doUserId)) { + if (!pmi.isAdminSuspendingAnyPackages(doUserId)) { Slogf.i(LOG_TAG, "DO is not suspending any apps."); return; } @@ -2480,7 +2480,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { poAdmin.mSuspendPersonalApps = true; } else { Slogf.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps."); - pmi.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, doUserId); + pmi.unsuspendAdminSuspendedPackages(doUserId); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index 6570ce1cd500..506dbe8c48c4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -16,8 +16,6 @@ package com.android.server.devicepolicy; -import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; @@ -287,7 +285,7 @@ final class PolicyEnforcerCallbacks { suspendPersonalAppsInPackageManager(context, userId); } else { LocalServices.getService(PackageManagerInternal.class) - .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, userId); + .unsuspendAdminSuspendedPackages(userId); } }); return true; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java index b396cf498a67..40d3d5ca9fd9 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -45,15 +45,19 @@ import android.annotation.NonNull; import android.app.PropertyInvalidatedCache; import android.content.ComponentName; import android.content.pm.ApplicationInfo; +import android.content.pm.Flags; import android.content.pm.PackageManager; +import android.content.pm.SharedLibraryInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; +import android.content.pm.UserPackage; import android.os.BaseBundle; import android.os.Message; import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -67,6 +71,7 @@ import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.server.LocalServices; +import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.ArchiveState; @@ -85,6 +90,7 @@ import com.google.common.truth.Truth; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -109,6 +115,9 @@ import java.util.concurrent.CountDownLatch; @RunWith(AndroidJUnit4.class) @SmallTest public class PackageManagerSettingsTests { + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String PACKAGE_NAME_1 = "com.android.app1"; private static final String PACKAGE_NAME_2 = "com.android.app2"; private static final String PACKAGE_NAME_3 = "com.android.app3"; @@ -140,6 +149,7 @@ public class PackageManagerSettingsTests { public void setup() { // Disable binder caches in this process. PropertyInvalidatedCache.disableForTestMode(); + } @Before @@ -161,6 +171,107 @@ public class PackageManagerSettingsTests { deleteFolder(InstrumentationRegistry.getContext().getFilesDir()); } + @Test + public void testApplicationInfoForUseSdkOptionalEnabled() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_SDK_LIB_INDEPENDENCE); + + // Create basic information for SDK lib + final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); + ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()) + .setUid(ps1.getAppId()) + .setSystem(true) + .hideAsFinal()); + ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" }); + ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 }); + ps1.setUsesSdkLibrariesOptional(new boolean[] {true}); + ps1.addUsesLibraryInfo(new SharedLibraryInfo("path1", + "packageName1", + Collections.emptyList(), + "com.example.sdk.one", + 12 /*version*/, + SharedLibraryInfo.TYPE_SDK_PACKAGE, + null /*declaringPackage*/, + null /*dependentPackages*/, + null /*dependencies*/, + false /*isNative*/)); + ps1.addUsesLibraryInfo(new SharedLibraryInfo("path11", + "packageName11", + Collections.emptyList(), + "com.example.sdk.oneone", + 1212 /*version*/, + SharedLibraryInfo.TYPE_STATIC, + null /*declaringPackage*/, + null /*dependentPackages*/, + null /*dependencies*/, + false /*isNative*/)); + ApplicationInfo appInfo1 = PackageInfoUtils.generateApplicationInfo(ps1.getAndroidPackage(), + 0 /*flags*/, ps1.getUserStateOrDefault(0), 0 /*userId*/, + ps1); + assertThat(appInfo1, notNullValue()); + assertThat(appInfo1.sharedLibraryInfos, notNullValue()); + assertThat(appInfo1.optionalSharedLibraryInfos, notNullValue()); + assertEquals(appInfo1.sharedLibraryInfos.get(0).getName(), "com.example.sdk.one"); + assertEquals(appInfo1.optionalSharedLibraryInfos.get(0).getName(), "com.example.sdk.one"); + + final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); + ps2.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_2).hideAsParsed()) + .setUid(ps2.getAppId()) + .setSystem(true) + .hideAsFinal()); + ps2.setUsesSdkLibraries(new String[] { "com.example.sdk.two" }); + ps2.setUsesSdkLibrariesVersionsMajor(new long[] { 34 }); + ps2.setUsesSdkLibrariesOptional(new boolean[] {false}); + ps2.addUsesLibraryInfo(new SharedLibraryInfo("path2", + "packageName2", + Collections.emptyList(), + "com.example.sdk.two", + 34 /*version*/, + SharedLibraryInfo.TYPE_SDK_PACKAGE, + null /*declaringPackage*/, + null /*dependentPackages*/, + null /*dependencies*/, + false /*isNative*/)); + ApplicationInfo appInfo2 = PackageInfoUtils.generateApplicationInfo(ps2.getAndroidPackage(), + 0 /*flags*/, ps2.getUserStateOrDefault(0), 0 /*userId*/, + ps2); + assertThat(appInfo2, notNullValue()); + assertThat(appInfo2.sharedLibraryInfos, notNullValue()); + assertThat(appInfo2.optionalSharedLibraryInfos, nullValue()); + assertEquals(appInfo2.sharedLibraryInfos.get(0).getName(), "com.example.sdk.two"); + } + + @Test + public void testApplicationInfoForUseSdkOptionalDisabled() throws Exception { + mSetFlagsRule.disableFlags(Flags.FLAG_SDK_LIB_INDEPENDENCE); + + // Create basic information for SDK lib + final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); + ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()) + .setUid(ps1.getAppId()) + .setSystem(true) + .hideAsFinal()); + ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" }); + ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 }); + ps1.setUsesSdkLibrariesOptional(new boolean[] {true}); + ps1.addUsesLibraryInfo(new SharedLibraryInfo("path1", + "packageName1", + Collections.emptyList(), + "com.example.sdk.one", + 12 /*version*/, + SharedLibraryInfo.TYPE_SDK_PACKAGE, + null /*declaringPackage*/, + null /*dependentPackages*/, + null /*dependencies*/, + false /*isNative*/)); + ApplicationInfo appInfo1 = PackageInfoUtils.generateApplicationInfo(ps1.getAndroidPackage(), + 0 /*flags*/, ps1.getUserStateOrDefault(0), 0 /*userId*/, + ps1); + assertThat(appInfo1, notNullValue()); + assertThat(appInfo1.sharedLibraryInfos, notNullValue()); + assertThat(appInfo1.optionalSharedLibraryInfos, nullValue()); + assertEquals(appInfo1.sharedLibraryInfos.get(0).getName(), "com.example.sdk.one"); + } + /** make sure our initialized KeySetManagerService metadata matches packages.xml */ @Test public void testReadKeySetSettings() throws Exception { @@ -315,7 +426,7 @@ public class PackageManagerSettingsTests { PackageUserStateInternal packageUserState1 = ps1.readUserState(0); assertThat(packageUserState1.isSuspended(), is(true)); assertThat(packageUserState1.getSuspendParams().size(), is(1)); - assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android")); + assertThat(packageUserState1.getSuspendParams().keyAt(0), is(UserPackage.of(0, "android"))); assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue())); assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(), is(nullValue())); @@ -327,7 +438,7 @@ public class PackageManagerSettingsTests { packageUserState1 = ps1.readUserState(0); assertThat(packageUserState1.isSuspended(), is(true)); assertThat(packageUserState1.getSuspendParams().size(), is(1)); - assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android")); + assertThat(packageUserState1.getSuspendParams().keyAt(0), is(UserPackage.of(0, "android"))); assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue())); assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(), is(nullValue())); @@ -362,7 +473,8 @@ public class PackageManagerSettingsTests { watcher.verifyNoChangeReported("readUserState"); assertThat(packageUserState1.isSuspended(), is(true)); assertThat(packageUserState1.getSuspendParams().size(), is(1)); - assertThat(packageUserState1.getSuspendParams().keyAt(0), is(PACKAGE_NAME_3)); + assertThat(packageUserState1.getSuspendParams().keyAt(0), + is(UserPackage.of(0, PACKAGE_NAME_3))); final SuspendParams params = packageUserState1.getSuspendParams().valueAt(0); watcher.verifyNoChangeReported("fetch user state"); assertThat(params, is(notNullValue())); @@ -413,19 +525,24 @@ public class PackageManagerSettingsTests { .setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND) .build(); - ps1.modifyUserState(0).putSuspendParams("suspendingPackage1", + UserPackage suspender1 = UserPackage.of(0, "suspendingPackage1"); + UserPackage suspender2 = UserPackage.of(0, "suspendingPackage2"); + UserPackage suspender3 = UserPackage.of(0, "suspendingPackage3"); + UserPackage irrelevantSuspender = UserPackage.of(0, "irrelevant"); + + ps1.modifyUserState(0).putSuspendParams(suspender1, new SuspendParams(dialogInfo1, appExtras1, launcherExtras1)); - ps1.modifyUserState(0).putSuspendParams("suspendingPackage2", + ps1.modifyUserState(0).putSuspendParams(suspender2, new SuspendParams(dialogInfo2, appExtras2, launcherExtras2)); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1); watcher.verifyChangeReported("put package 1"); - ps2.modifyUserState(0).putSuspendParams("suspendingPackage3", + ps2.modifyUserState(0).putSuspendParams(suspender3, new SuspendParams(null, appExtras1, null)); settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2); watcher.verifyChangeReported("put package 2"); - ps3.modifyUserState(0).removeSuspension("irrelevant"); + ps3.modifyUserState(0).removeSuspension(irrelevantSuspender); settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3); watcher.verifyChangeReported("put package 3"); @@ -450,7 +567,7 @@ public class PackageManagerSettingsTests { assertThat(readPus1.getSuspendParams().size(), is(2)); watcher.verifyNoChangeReported("read package param"); - assertThat(readPus1.getSuspendParams().keyAt(0), is("suspendingPackage1")); + assertThat(readPus1.getSuspendParams().keyAt(0), is(suspender1)); final SuspendParams params11 = readPus1.getSuspendParams().valueAt(0); watcher.verifyNoChangeReported("read package param"); assertThat(params11, is(notNullValue())); @@ -460,7 +577,7 @@ public class PackageManagerSettingsTests { is(true)); watcher.verifyNoChangeReported("read package param"); - assertThat(readPus1.getSuspendParams().keyAt(1), is("suspendingPackage2")); + assertThat(readPus1.getSuspendParams().keyAt(1), is(suspender2)); final SuspendParams params12 = readPus1.getSuspendParams().valueAt(1); assertThat(params12, is(notNullValue())); assertThat(params12.getDialogInfo(), is(dialogInfo2)); @@ -473,7 +590,7 @@ public class PackageManagerSettingsTests { .readUserState(0); assertThat(readPus2.isSuspended(), is(true)); assertThat(readPus2.getSuspendParams().size(), is(1)); - assertThat(readPus2.getSuspendParams().keyAt(0), is("suspendingPackage3")); + assertThat(readPus2.getSuspendParams().keyAt(0), is(suspender3)); final SuspendParams params21 = readPus2.getSuspendParams().valueAt(0); assertThat(params21, is(notNullValue())); assertThat(params21.getDialogInfo(), is(nullValue())); @@ -1024,7 +1141,8 @@ public class PackageManagerSettingsTests { .setNeutralButtonText(0x11220003) .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS) .build(); - origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1", + origPkgSetting01.modifyUserState(0).putSuspendParams( + UserPackage.of(0, "suspendingPackage1"), new SuspendParams(dialogInfo1, appExtras1, launcherExtras1)); origPkgSetting01.setPkg(mockAndroidPackage(origPkgSetting01)); final PackageSetting testPkgSetting01 = new PackageSetting( diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java index c0c70321c79b..978044045ab3 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.SuspendDialogInfo; +import android.content.pm.UserPackage; import android.content.pm.overlay.OverlayPaths; import android.os.PersistableBundle; import android.platform.test.annotations.Presubmit; @@ -89,7 +90,7 @@ public class PackageUserStateTest { assertThat(testUserState.equals(oldUserState), is(false)); oldUserState = new PackageUserStateImpl(); - oldUserState.putSuspendParams("suspendingPackage", + oldUserState.putSuspendParams(UserPackage.of(0, "suspendingPackage"), new SuspendParams(null, new PersistableBundle(), null)); assertThat(testUserState.equals(oldUserState), is(false)); @@ -220,6 +221,8 @@ public class PackageUserStateTest { final PersistableBundle launcherExtras2 = createPersistableBundle(null, 0, "name", "launcherExtras2", null, 0); + final int suspendingUser1 = 0; + final int suspendingUser2 = 10; final String suspendingPackage1 = "package1"; final String suspendingPackage2 = "package2"; @@ -230,12 +233,12 @@ public class PackageUserStateTest { .setMessage("dialogMessage2") .build(); - final ArrayMap<String, SuspendParams> paramsMap1 = new ArrayMap<>(); - paramsMap1.put(suspendingPackage1, createSuspendParams(dialogInfo1, appExtras1, - launcherExtras1)); - final ArrayMap<String, SuspendParams> paramsMap2 = new ArrayMap<>(); - paramsMap2.put(suspendingPackage2, createSuspendParams(dialogInfo2, - appExtras2, launcherExtras2)); + final ArrayMap<UserPackage, SuspendParams> paramsMap1 = new ArrayMap<>(); + paramsMap1.put(UserPackage.of(suspendingUser1, suspendingPackage1), + createSuspendParams(dialogInfo1, appExtras1, launcherExtras1)); + final ArrayMap<UserPackage, SuspendParams> paramsMap2 = new ArrayMap<>(); + paramsMap2.put(UserPackage.of(suspendingUser2, suspendingPackage2), + createSuspendParams(dialogInfo2, appExtras2, launcherExtras2)); final PackageUserStateImpl testUserState1 = new PackageUserStateImpl(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt new file mode 100644 index 000000000000..ebb4f1889cd6 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.mode + +import android.content.Context +import android.content.ContextWrapper +import android.provider.Settings +import androidx.test.core.app.ApplicationProvider +import androidx.test.filters.SmallTest +import com.android.internal.util.test.FakeSettingsProvider +import com.android.server.display.feature.DisplayManagerFlags +import com.android.server.testutils.TestHandler +import com.google.common.truth.Truth.assertThat +import com.google.testing.junit.testparameterinjector.TestParameter +import com.google.testing.junit.testparameterinjector.TestParameterInjector +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito +import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(TestParameterInjector::class) +class SettingsObserverTest { + + @get:Rule + val mockitoRule = MockitoJUnit.rule() + + @get:Rule + val settingsProviderRule = FakeSettingsProvider.rule() + + private lateinit var spyContext: Context + private val mockInjector = mock<DisplayModeDirector.Injector>() + private val mockFlags = mock<DisplayManagerFlags>() + + private val testHandler = TestHandler(null) + + @Before + fun setUp() { + spyContext = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) + } + + @Test + fun testLowPowerMode(@TestParameter testCase: SettingsObserverTestCase) { + whenever(mockFlags.isVsyncLowPowerVoteEnabled).thenReturn(testCase.vsyncLowPowerVoteEnabled) + whenever(spyContext.contentResolver) + .thenReturn(settingsProviderRule.mockContentResolver(null)) + val lowPowerModeSetting = if (testCase.lowPowerModeEnabled) 1 else 0 + Settings.Global.putInt( + spyContext.contentResolver, Settings.Global.LOW_POWER_MODE, lowPowerModeSetting) + + val displayModeDirector = DisplayModeDirector( + spyContext, testHandler, mockInjector, mockFlags) + val settingsObserver = displayModeDirector.SettingsObserver( + spyContext, testHandler, testCase.dvrrSupported, mockFlags) + + settingsObserver.onChange( + false, Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), 1) + + assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID, + Vote.PRIORITY_LOW_POWER_MODE)).isEqualTo(testCase.expectedVote) + } + + enum class SettingsObserverTestCase( + val dvrrSupported: Boolean, + val vsyncLowPowerVoteEnabled: Boolean, + val lowPowerModeEnabled: Boolean, + internal val expectedVote: Vote? + ) { + ALL_ENABLED(true, true, true, + SupportedModesVote(listOf( + SupportedModesVote.SupportedMode(60f, 240f), + SupportedModesVote.SupportedMode(60f, 60f) + ))), + LOW_POWER_OFF(true, true, false, null), + DVRR_NOT_SUPPORTED_LOW_POWER_ON(false, true, true, + RefreshRateVote.RenderVote(0f, 60f)), + DVRR_NOT_SUPPORTED_LOW_POWER_OFF(false, true, false, null), + VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(true, false, true, + RefreshRateVote.RenderVote(0f, 60f)), + VSYNC_VOTE_DISABLED_LOW_POWER_OFF(true, false, false, null), + } +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index ae53e707a7cc..7444403f88c8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -19,6 +19,7 @@ package com.android.server.pm import android.app.AppOpsManager import android.content.Intent import android.content.pm.SuspendDialogInfo +import android.content.pm.UserPackage import android.os.Binder import android.os.PersistableBundle import com.android.server.testutils.any @@ -41,12 +42,18 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { .thenReturn(AppOpsManager.MODE_DEFAULT) } + companion object { + val doUserPackage = UserPackage.of(TEST_USER_ID, DEVICE_OWNER_PACKAGE) + val platformUserPackage = UserPackage.of(TEST_USER_ID, PLATFORM_PACKAGE_NAME) + val testUserPackage1 = UserPackage.of(TEST_USER_ID, TEST_PACKAGE_1) + } + @Test fun setPackagesSuspended() { val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2) val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() @@ -63,14 +70,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_emptyPackageName() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), null /* packageNames */, true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).isNull() failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOfNulls(0), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).isEmpty() @@ -80,7 +87,8 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_callerIsNotAllowed() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, + null /* launcherExtras */, null /* dialogInfo */, + testUserPackage1, TEST_USER_ID, Binder.getCallingUid(), false /* quarantined */) assertThat(failedNames).asList().hasSize(1) @@ -91,7 +99,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_callerSuspendItself() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).asList().hasSize(1) @@ -102,7 +110,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_nonexistentPackage() { val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).asList().hasSize(1) @@ -115,7 +123,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE) val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), knownPackages, true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */)!! assertThat(failedNames.size).isEqualTo(knownPackages.size) @@ -129,14 +137,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() Mockito.clearInvocations(broadcastHelper) assertThat(failedNames).isEmpty() failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, false /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() @@ -184,7 +192,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_1) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */, - null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -202,22 +210,22 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, appExtras, null /* launcherExtras */, - null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() Mockito.clearInvocations(broadcastHelper) assertThat(failedNames).isEmpty() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) + TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull() assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(), TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull() suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), - targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, + targetPackages, { suspender -> suspender.packageName == DEVICE_OWNER_PACKAGE }, TEST_USER_ID) testHandler.flush() @@ -243,7 +251,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -258,7 +266,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun isPackageSuspended() { var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, + null /* launcherExtras */, null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() @@ -273,13 +281,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2) var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) } @Test @@ -290,57 +298,57 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { // Suspend. var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).isEmpty() testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) // Suspend by system. failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, PLATFORM_PACKAGE_NAME, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, platformUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) assertThat(failedNames).isEmpty() testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage) // QAS by package1. failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, - null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid, + null /* dialogInfo */, testUserPackage1, TEST_USER_ID, deviceOwnerUid, true /* quarantined */) assertThat(failedNames).isEmpty() testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(TEST_PACKAGE_1) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(testUserPackage1) // Un-QAS by package1. suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), - targetPackages, { suspendingPackage -> suspendingPackage == TEST_PACKAGE_1 }, + targetPackages, { suspendingPackage -> suspendingPackage == testUserPackage1 }, TEST_USER_ID) testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage) // Un-suspend by system. suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), - targetPackages, { suspendingPackage -> suspendingPackage == PLATFORM_PACKAGE_NAME }, + targetPackages, { suspender -> suspender.packageName == PLATFORM_PACKAGE_NAME }, TEST_USER_ID) testHandler.flush() assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), - TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(doUserPackage) // Unsuspend. suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), - targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, + targetPackages, { suspendingPackage -> suspendingPackage == doUserPackage }, TEST_USER_ID) testHandler.flush() @@ -354,13 +362,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { .setTitle(TEST_PACKAGE_1).build() var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */, - null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID, + null /* launcherExtras */, dialogInfo, doUserPackage, TEST_USER_ID, deviceOwnerUid, false /* quarantined */) testHandler.flush() assertThat(failedNames).isEmpty() val result = suspendPackageHelper.getSuspendedDialogInfo(pms.snapshotComputer(), - TEST_PACKAGE_1, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!! + TEST_PACKAGE_1, doUserPackage, TEST_USER_ID, deviceOwnerUid)!! assertThat(result.title).isEqualTo(TEST_PACKAGE_1) } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java index a78f2dcf2ab2..3b5cae328b3c 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java @@ -23,6 +23,8 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS; +import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR; + import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertThrows; @@ -41,6 +43,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.hardware.biometrics.AuthenticationStateListener; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.Flags; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; @@ -108,6 +111,7 @@ public class AuthServiceTest { @Mock IFaceService mFaceService; @Mock + AppOpsManager mAppOpsManager; @Mock private VirtualDeviceManagerInternal mVdmInternal; @@ -404,6 +408,23 @@ public class AuthServiceTest { eq(TEST_OP_PACKAGE_NAME)); } + @Test + public void testRegisterAuthenticationStateListener_callsFingerprintService() + throws Exception { + mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR); + setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */); + + mAuthService = new AuthService(mContext, mInjector); + mAuthService.onStart(); + + final AuthenticationStateListener listener = mock(AuthenticationStateListener.class); + + mAuthService.mImpl.registerAuthenticationStateListener(listener); + + waitForIdle(); + verify(mFingerprintService).registerAuthenticationStateListener( + eq(listener)); + } @Test public void testRegisterKeyguardCallback_callsBiometricServiceRegisterKeyguardCallback() diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java index 5012335b533f..94cb860ae710 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors; +import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -23,10 +25,11 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.hardware.biometrics.BiometricOverlayConstants; +import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; import androidx.test.filters.SmallTest; @@ -40,6 +43,7 @@ import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.List; +@RequiresFlagsDisabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR) @Presubmit @SmallTest public class SensorOverlaysTest { @@ -97,7 +101,7 @@ public class SensorOverlaysTest { private void testShow(IUdfpsOverlayController udfps, ISidefpsController sidefps) throws Exception { final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps); - final int reason = BiometricOverlayConstants.REASON_UNKNOWN; + final int reason = BiometricRequestConstants.REASON_UNKNOWN; sensorOverlays.show(SENSOR_ID, reason, mAcquisitionClient); if (udfps != null) { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index 79a528c59f49..c24227fcd1f7 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -18,6 +18,8 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED; +import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -56,6 +58,7 @@ import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.TestableContext; import androidx.test.filters.SmallTest; @@ -68,6 +71,7 @@ import com.android.server.biometrics.log.CallbackWithProbe; import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.AuthSessionCoordinator; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutTracker; @@ -91,6 +95,8 @@ import java.util.function.Consumer; @SmallTest public class FingerprintAuthenticationClientTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int SENSOR_ID = 4; private static final int USER_ID = 8; private static final long OP_ID = 7; @@ -128,6 +134,8 @@ public class FingerprintAuthenticationClientTest { @Mock private ISidefpsController mSideFpsController; @Mock + private AuthenticationStateListeners mAuthenticationStateListeners; + @Mock private FingerprintSensorPropertiesInternal mSensorProps; @Mock private ClientMonitorCallback mCallback; @@ -384,6 +392,7 @@ public class FingerprintAuthenticationClientTest { private void showHideOverlay(Consumer<FingerprintAuthenticationClient> block) throws RemoteException { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR); final FingerprintAuthenticationClient client = createClient(); client.start(mCallback); @@ -398,6 +407,49 @@ public class FingerprintAuthenticationClientTest { } @Test + public void showHideOverlay_cancel_sidefpsControllerRemovalRefactor() throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.cancel()); + } + + @Test + public void showHideOverlay_stop_sidefpsControllerRemovalRefactor() throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.stopHalOperation()); + } + + @Test + public void showHideOverlay_error_sidefpsControllerRemovalRefactor() throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onError(0, 0)); + verify(mCallback).onClientFinished(any(), eq(false)); + } + + @Test + public void showHideOverlay_lockout_sidefpsControllerRemovalRefactor() throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onLockoutTimed(5000)); + } + + @Test + public void showHideOverlay_lockoutPerm_sidefpsControllerRemovalRefactor() + throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onLockoutPermanent()); + } + + private void showHideOverlay_sidefpsControllerRemovalRefactor( + Consumer<FingerprintAuthenticationClient> block) throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR); + final FingerprintAuthenticationClient client = createClient(); + + client.start(mCallback); + + verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any()); + verify(mAuthenticationStateListeners).onAuthenticationStarted(anyInt()); + + block.accept(client); + + verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt()); + verify(mAuthenticationStateListeners).onAuthenticationStopped(); + } + + @Test public void cancelsAuthWhenNotInForeground() throws Exception { final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo(); topTask.topActivity = new ComponentName("other", "thing"); @@ -502,7 +554,8 @@ public class FingerprintAuthenticationClientTest { mBiometricLogger, mBiometricContext, true /* isStrongBiometric */, null /* taskStackListener */, - mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication, + mUdfpsOverlayController, mSideFpsController, mAuthenticationStateListeners, + allowBackgroundAuthentication, mSensorProps, new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock, lockoutTracker) { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java index c7eb1db3e74b..e7d4a2e463f7 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; @@ -38,6 +40,7 @@ import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.TestableContext; import androidx.test.filters.SmallTest; @@ -48,6 +51,7 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.log.Probe; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -68,6 +72,8 @@ import java.util.function.Consumer; @SmallTest public class FingerprintEnrollClientTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final byte[] HAT = new byte[69]; private static final int USER_ID = 8; private static final long REQUEST_ID = 9; @@ -98,6 +104,8 @@ public class FingerprintEnrollClientTest { @Mock private ISidefpsController mSideFpsController; @Mock + private AuthenticationStateListeners mAuthenticationStateListeners; + @Mock private FingerprintSensorPropertiesInternal mSensorProps; @Mock private ClientMonitorCallback mCallback; @@ -271,6 +279,7 @@ public class FingerprintEnrollClientTest { private void showHideOverlay(Consumer<FingerprintEnrollClient> block) throws RemoteException { + mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR); final FingerprintEnrollClient client = createClient(); client.start(mCallback); @@ -284,6 +293,44 @@ public class FingerprintEnrollClientTest { verify(mSideFpsController).hide(anyInt()); } + @Test + public void showHideOverlay_cancel_sidefpsControllerRemovalRefactor() throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.cancel()); + } + + @Test + public void showHideOverlay_stop_sidefpsControllerRemovalRefactor() throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.stopHalOperation()); + } + + @Test + public void showHideOverlay_error_sidefpsControllerRemovalRefactor() throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor(c -> c.onError(0, 0)); + verify(mCallback).onClientFinished(any(), eq(false)); + } + + @Test + public void showHideOverlay_result_sidefpsControllerRemovalRefactor() throws RemoteException { + showHideOverlay_sidefpsControllerRemovalRefactor( + c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0)); + } + + private void showHideOverlay_sidefpsControllerRemovalRefactor( + Consumer<FingerprintEnrollClient> block) throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR); + final FingerprintEnrollClient client = createClient(); + + client.start(mCallback); + + verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any()); + verify(mAuthenticationStateListeners).onAuthenticationStarted(anyInt()); + + block.accept(client); + + verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt()); + verify(mAuthenticationStateListeners).onAuthenticationStopped(); + } + private FingerprintEnrollClient createClient() throws RemoteException { return createClient(500); } @@ -296,6 +343,7 @@ public class FingerprintEnrollClientTest { mClientMonitorCallbackConverter, 0 /* userId */, HAT, "owner", mBiometricUtils, 8 /* sensorId */, mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController, - mSideFpsController, 6 /* maxTemplatesPerUser */, FingerprintManager.ENROLL_ENROLL); + mSideFpsController, mAuthenticationStateListeners, 6 /* maxTemplatesPerUser */, + FingerprintManager.ENROLL_ENROLL); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java index 8f6efffcbff8..4cfb83fa1c69 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -43,6 +43,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; @@ -74,6 +75,8 @@ public class FingerprintProviderTest { @Mock private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @Mock + private AuthenticationStateListeners mAuthenticationStateListeners; + @Mock private BiometricStateCallback mBiometricStateCallback; @Mock private BiometricContext mBiometricContext; @@ -110,8 +113,9 @@ public class FingerprintProviderTest { mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); mFingerprintProvider = new FingerprintProvider(mContext, - mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher, - mGestureAvailabilityDispatcher, mBiometricContext, mDaemon); + mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG, + mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext, + mDaemon); } @Test diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java index b32b89aaefb0..0d3f1921c947 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java @@ -40,6 +40,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.LockoutResetDispatcher; @@ -70,6 +71,8 @@ public class Fingerprint21Test { @Mock private BiometricScheduler mScheduler; @Mock + private AuthenticationStateListeners mAuthenticationStateListeners; + @Mock private BiometricStateCallback mBiometricStateCallback; @Mock private BiometricContext mBiometricContext; @@ -102,9 +105,10 @@ public class Fingerprint21Test { componentInfo, FingerprintSensorProperties.TYPE_UNKNOWN, resetLockoutRequiresHardwareAuthToken); - mFingerprint21 = new TestableFingerprint21(mContext, mBiometricStateCallback, sensorProps, - mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, - mHalResultController, mBiometricContext); + mFingerprint21 = new TestableFingerprint21(mContext, mBiometricStateCallback, + mAuthenticationStateListeners, sensorProps, mScheduler, + new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, mHalResultController, + mBiometricContext); } @Test @@ -126,13 +130,14 @@ public class Fingerprint21Test { TestableFingerprint21(@NonNull Context context, @NonNull BiometricStateCallback biometricStateCallback, + @NonNull AuthenticationStateListeners authenticationStateListeners, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull BiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull HalResultController controller, @NonNull BiometricContext biometricContext) { - super(context, biometricStateCallback, sensorProps, scheduler, handler, - lockoutResetDispatcher, controller, biometricContext); + super(context, biometricStateCallback, authenticationStateListeners, sensorProps, + scheduler, handler, lockoutResetDispatcher, controller, biometricContext); } @Override diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index 943a9c4759c4..1dd64ffa5dde 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -18,7 +18,6 @@ package com.android.server.devicepolicy; import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile; -import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -222,21 +221,21 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R); // Pretend some packages are suspended. - when(getServices().packageManagerInternal.isSuspendingAnyPackages( - PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true); + when(getServices().packageManagerInternal.isAdminSuspendingAnyPackages( + USER_SYSTEM)).thenReturn(true); final DevicePolicyManagerServiceTestable dpms = bootDpmsUp(); verify(getServices().packageManagerInternal, never()) - .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM); + .unsuspendAdminSuspendedPackages(USER_SYSTEM); sendBroadcastWithUser(dpms, Intent.ACTION_USER_STARTED, USER_SYSTEM); // Verify that actual package suspension state is not modified after user start verify(getServices().packageManagerInternal, never()) - .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM); + .unsuspendAdminSuspendedPackages(USER_SYSTEM); verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser( - any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt()); + any(), anyBoolean(), any(), any(), any(), anyInt(), any(), anyInt(), anyInt()); final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext); poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); @@ -255,14 +254,14 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.Q); // Pretend some packages are suspended. - when(getServices().packageManagerInternal.isSuspendingAnyPackages( - PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true); + when(getServices().packageManagerInternal.isAdminSuspendingAnyPackages( + USER_SYSTEM)).thenReturn(true); final DevicePolicyManagerServiceTestable dpms = bootDpmsUp(); // Verify that apps get unsuspended. verify(getServices().packageManagerInternal) - .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM); + .unsuspendAdminSuspendedPackages(USER_SYSTEM); final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext); poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index f4dac2c10d0f..24704034ae0c 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -63,7 +63,6 @@ import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH; import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE; import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION; import static com.android.server.devicepolicy.DpmMockContext.CALLER_USER_HANDLE; -import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.testutils.TestUtils.assertExpectException; import static com.google.common.truth.Truth.assertThat; @@ -5080,7 +5079,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().iwindowManager).refreshScreenCaptureDisabled(); // Unsuspend personal apps verify(getServices().packageManagerInternal) - .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM); + .unsuspendAdminSuspendedPackages(UserHandle.USER_SYSTEM); verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, FLAG_ENABLE_WORK_PROFILE_TELEPHONY, "false", false); @@ -7535,7 +7534,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED)); // Verify that the apps are NOT unsuspeded. verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser( - any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt()); + any(), eq(false), any(), any(), any(), anyInt(), any(), anyInt(), anyInt()); // Verify that DPC is invoked to check policy compliance. verify(mContext.spiedContext).startActivityAsUser( MockUtils.checkIntentAction(ACTION_CHECK_POLICY_COMPLIANCE), diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java index fe37f4241d8e..b3d25f2eef25 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java @@ -47,6 +47,10 @@ import java.util.Map; @Presubmit @RunWith(AndroidJUnit4.class) public final class OverrideRequestControllerTest { + + private static final DeviceState TEST_DEVICE_STATE_ZERO = new DeviceState(0, "TEST_STATE", 0); + private static final DeviceState TEST_DEVICE_STATE_ONE = new DeviceState(1, "TEST_STATE", 0); + private TestStatusChangeListener mStatusListener; private OverrideRequestController mController; @@ -59,7 +63,7 @@ public final class OverrideRequestControllerTest { @Test public void addRequest() { OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(request)); mController.addRequest(request); @@ -69,14 +73,14 @@ public final class OverrideRequestControllerTest { @Test public void addRequest_cancelExistingRequestThroughNewRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(firstRequest)); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(secondRequest)); mController.addRequest(secondRequest); @@ -87,7 +91,7 @@ public final class OverrideRequestControllerTest { @Test public void addRequest_cancelActiveRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); mController.addRequest(firstRequest); @@ -101,7 +105,7 @@ public final class OverrideRequestControllerTest { @Test public void addBaseStateRequest() { OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); assertNull(mStatusListener.getLastStatus(request)); mController.addBaseStateRequest(request); @@ -111,14 +115,14 @@ public final class OverrideRequestControllerTest { @Test public void addBaseStateRequest_cancelExistingBaseStateRequestThroughNewRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); assertNull(mStatusListener.getLastStatus(firstRequest)); mController.addBaseStateRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); assertNull(mStatusListener.getLastStatus(secondRequest)); mController.addBaseStateRequest(secondRequest); @@ -129,7 +133,7 @@ public final class OverrideRequestControllerTest { @Test public void addBaseStateRequest_cancelActiveBaseStateRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addBaseStateRequest(firstRequest); @@ -143,13 +147,13 @@ public final class OverrideRequestControllerTest { @Test public void handleBaseStateChanged() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, + TEST_DEVICE_STATE_ZERO, DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); @@ -169,11 +173,11 @@ public final class OverrideRequestControllerTest { @Test public void handleProcessDied() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); @@ -192,11 +196,11 @@ public final class OverrideRequestControllerTest { mController.setStickyRequestsAllowed(true); OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); @@ -215,11 +219,11 @@ public final class OverrideRequestControllerTest { @Test public void handleNewSupportedStates() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); @@ -242,7 +246,7 @@ public final class OverrideRequestControllerTest { @Test public void cancelOverrideRequestsTest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java index fb7857494515..0d25faf2bcf1 100644 --- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java @@ -60,6 +60,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.inputmethod.StartInputFlags; +import com.google.common.truth.Truth; + import org.junit.Test; import org.junit.runner.RunWith; @@ -1336,6 +1338,54 @@ public class InputMethodUtilsTest { } } + private static void verifySplitEnabledImeStr(@NonNull String enabledImeStr, + @NonNull String... expected) { + final ArrayList<String> actual = new ArrayList<>(); + InputMethodUtils.splitEnabledImeStr(enabledImeStr, actual::add); + if (expected.length == 0) { + Truth.assertThat(actual).isEmpty(); + } else { + Truth.assertThat(actual).containsExactlyElementsIn(expected); + } + } + + @Test + public void testSplitEnabledImeStr() { + verifySplitEnabledImeStr(""); + verifySplitEnabledImeStr("com.android/.ime1", "com.android/.ime1"); + verifySplitEnabledImeStr("com.android/.ime1;1;2;3", "com.android/.ime1"); + verifySplitEnabledImeStr("com.android/.ime1;1;2;3:com.android/.ime2", + "com.android/.ime1", "com.android/.ime2"); + verifySplitEnabledImeStr("com.android/.ime1:com.android/.ime2", + "com.android/.ime1", "com.android/.ime2"); + verifySplitEnabledImeStr("com.android/.ime1:com.android/.ime2:com.android/.ime3", + "com.android/.ime1", "com.android/.ime2", "com.android/.ime3"); + verifySplitEnabledImeStr("com.android/.ime1;1:com.android/.ime2;1:com.android/.ime3;1", + "com.android/.ime1", "com.android/.ime2", "com.android/.ime3"); + } + + @Test + public void testConcatEnabledImeIds() { + Truth.assertThat(InputMethodUtils.concatEnabledImeIds("")).isEmpty(); + Truth.assertThat(InputMethodUtils.concatEnabledImeIds("", "com.android/.ime1")) + .isEqualTo("com.android/.ime1"); + Truth.assertThat(InputMethodUtils.concatEnabledImeIds( + "com.android/.ime1", "com.android/.ime1")) + .isEqualTo("com.android/.ime1"); + Truth.assertThat(InputMethodUtils.concatEnabledImeIds( + "com.android/.ime1", "com.android/.ime2")) + .isEqualTo("com.android/.ime1:com.android/.ime2"); + Truth.assertThat(InputMethodUtils.concatEnabledImeIds( + "com.android/.ime1", "com.android/.ime2", "com.android/.ime3")) + .isEqualTo("com.android/.ime1:com.android/.ime2:com.android/.ime3"); + Truth.assertThat(InputMethodUtils.concatEnabledImeIds( + "com.android/.ime1:com.android/.ime2", "com.android/.ime1")) + .isEqualTo("com.android/.ime1:com.android/.ime2"); + Truth.assertThat(InputMethodUtils.concatEnabledImeIds( + "com.android/.ime1:com.android/.ime2", "com.android/.ime3")) + .isEqualTo("com.android/.ime1:com.android/.ime2:com.android/.ime3"); + } + @Test public void updateEnabledImeStringTest() { // No change cases diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 14b551ae0b22..776189eeb7c3 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -219,6 +219,7 @@ import android.provider.MediaStore; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.ConversationChannelWrapper; +import android.service.notification.DeviceEffectsApplier; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRankingUpdate; @@ -635,6 +636,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } private void initNMS() throws Exception { + initNMS(SystemService.PHASE_BOOT_COMPLETED); + } + + private void initNMS(int upToBootPhase) throws Exception { mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger, mNotificationInstanceIdSequence); @@ -662,13 +667,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), mTelecomManager, mLogger, mTestFlagResolver, mPermissionManager, mPowerManager, mPostNotificationTrackerFactory); + // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); - mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); + + if (upToBootPhase >= SystemService.PHASE_SYSTEM_SERVICES_READY) { + mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); + } + Mockito.reset(mHistoryManager); verify(mHistoryManager, never()).onBootPhaseAppsCanStart(); - mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper); - verify(mHistoryManager).onBootPhaseAppsCanStart(); + + if (upToBootPhase >= SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { + mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper); + verify(mHistoryManager).onBootPhaseAppsCanStart(); + } // TODO b/291907312: remove feature flag if (Flags.refactorAttentionHelper()) { @@ -914,7 +927,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel.setAllowBubbles(channelEnabled); } - private void setUpPrefsForHistory(int uid, boolean globalEnabled) { + private void setUpPrefsForHistory(int uid, boolean globalEnabled) throws Exception { + initNMS(SystemService.PHASE_ACTIVITY_MANAGER_READY); + // Sets NOTIFICATION_HISTORY_ENABLED setting for calling process uid Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0, uid); @@ -10090,7 +10105,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testHandleOnPackageRemoved_ClearsHistory() throws RemoteException { + public void testHandleOnPackageRemoved_ClearsHistory() throws Exception { // Enables Notification History setting setUpPrefsForHistory(mUid, true /* =enabled */); @@ -13209,6 +13224,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + @EnableFlags(android.app.Flags.FLAG_MODES_API) + public void setDeviceEffectsApplier_succeeds() throws Exception { + initNMS(SystemService.PHASE_SYSTEM_SERVICES_READY); + + mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class)); + // No exception! + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_API) + public void setDeviceEffectsApplier_tooLate_throws() throws Exception { + initNMS(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START); + + assertThrows(IllegalStateException.class, () -> + mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class))); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_API) + public void setDeviceEffectsApplier_calledTwice_throws() throws Exception { + initNMS(SystemService.PHASE_SYSTEM_SERVICES_READY); + + mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class)); + assertThrows(IllegalStateException.class, () -> + mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class))); + } + + @Test @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES) public void setNotificationPolicy_mappedToImplicitRule() throws RemoteException { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index 526201f9c1c6..670f9f697a5c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -49,6 +49,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; +import android.content.pm.UserPackage; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -195,7 +196,7 @@ public class ActivityStartInterceptorTest { mAInfo.applicationInfo.flags = FLAG_SUSPENDED; when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID)) - .thenReturn(PLATFORM_PACKAGE_NAME); + .thenReturn(UserPackage.of(TEST_USER_ID, PLATFORM_PACKAGE_NAME)); // THEN calling intercept returns true assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null)); @@ -227,9 +228,10 @@ public class ActivityStartInterceptorTest { .setMessage("Test Message") .setIcon(0x11110001) .build(); + UserPackage suspender = UserPackage.of(TEST_USER_ID, suspendingPackage); when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID)) - .thenReturn(suspendingPackage); - when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage, + .thenReturn(suspender); + when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspender, TEST_USER_ID)).thenReturn(dialogInfo); return dialogInfo; } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 98055fa6f0d5..8a9c05d07b26 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -117,6 +117,7 @@ import androidx.test.filters.SmallTest; import com.android.compatibility.common.util.DeviceConfigStateHelper; import com.android.internal.util.FrameworkStatsLog; import com.android.server.am.PendingIntentRecord; +import com.android.server.pm.PackageArchiver; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.wm.BackgroundActivityStartController.BalVerdict; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; @@ -421,6 +422,7 @@ public class ActivityStarterTests extends WindowTestsBase { doNothing().when(mMockPackageManager).grantImplicitAccess( anyInt(), any(), anyInt(), anyInt(), anyBoolean()); doNothing().when(mMockPackageManager).notifyPackageUse(anyString(), anyInt()); + doReturn(mock(PackageArchiver.class)).when(mMockPackageManager).getPackageArchiver(); final Intent intent = new Intent(); intent.addFlags(launchFlags); diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java index eac4d1682aa9..cc768bc00250 100644 --- a/telephony/java/android/telephony/CarrierRestrictionRules.java +++ b/telephony/java/android/telephony/CarrierRestrictionRules.java @@ -16,12 +16,16 @@ package android.telephony; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.service.carrier.CarrierIdentifier; +import android.telephony.TelephonyManager.CarrierRestrictionStatus; + +import com.android.internal.telephony.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -104,7 +108,7 @@ public final class CarrierRestrictionRules implements Parcelable { private int mCarrierRestrictionDefault; @MultiSimPolicy private int mMultiSimPolicy; - @TelephonyManager.CarrierRestrictionStatus + @CarrierRestrictionStatus private int mCarrierRestrictionStatus; private CarrierRestrictionRules() { @@ -293,8 +297,22 @@ public final class CarrierRestrictionRules implements Parcelable { return true; } - /** @hide */ - public int getCarrierRestrictionStatus() { + /** + * Get the carrier restriction status of the device. + * The return value of the API is as follows. + * <ul> + * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER} + * if the caller and the device locked by the network are same</li> + * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED} if the + * caller and the device locked by the network are different</li> + * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the + * device is not locked</li> + * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device + * locking state is unavailable or radio does not supports the feature</li> + * </ul> + */ + @FlaggedApi(Flags.FLAG_CARRIER_RESTRICTION_STATUS) + public @CarrierRestrictionStatus int getCarrierRestrictionStatus() { return mCarrierRestrictionStatus; } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f206987ddbf6..5520cf6f50ba 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -18025,16 +18025,16 @@ public class TelephonyManager { @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY) @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi - public void enableCellularIdentifierDisclosureNotifications(boolean enable) { + public void setEnableCellularIdentifierDisclosureNotifications(boolean enable) { try { ITelephony telephony = getITelephony(); if (telephony != null) { - telephony.enableCellularIdentifierDisclosureNotifications(enable); + telephony.setEnableCellularIdentifierDisclosureNotifications(enable); } else { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - Rlog.e(TAG, "enableCellularIdentifierDisclosureNotifications RemoteException", ex); + Rlog.e(TAG, "setEnableCellularIdentifierDisclosureNotifications RemoteException", ex); ex.rethrowFromSystemServer(); } } @@ -18051,16 +18051,16 @@ public class TelephonyManager { @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY) @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @SystemApi - public boolean isCellularIdentifierDisclosureNotificationEnabled() { + public boolean isCellularIdentifierDisclosureNotificationsEnabled() { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.isCellularIdentifierDisclosureNotificationEnabled(); + return telephony.isCellularIdentifierDisclosureNotificationsEnabled(); } else { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - Rlog.e(TAG, "isCellularIdentifierDisclosureNotificationEnabled RemoteException", ex); + Rlog.e(TAG, "isCellularIdentifierDisclosureNotificationsEnabled RemoteException", ex); ex.rethrowFromSystemServer(); } return false; diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 3e8787281f85..8679bd4baf2e 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1866,7 +1866,7 @@ public class ApnSetting implements Parcelable { private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT; private boolean mAlwaysOn; - private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR; + private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR | INFRASTRUCTURE_SATELLITE; private boolean mEsimBootstrapProvisioning; /** diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 397fb2d6db96..37752d0cecd8 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -3182,7 +3182,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.MODIFY_PHONE_STATE)") - void enableCellularIdentifierDisclosureNotifications(boolean enable); + void setEnableCellularIdentifierDisclosureNotifications(boolean enable); /** * Get whether or not cellular identifier disclosure notifications are enabled. @@ -3196,5 +3196,5 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)") - boolean isCellularIdentifierDisclosureNotificationEnabled(); + boolean isCellularIdentifierDisclosureNotificationsEnabled(); } diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt index 12a57d52491a..c8cac8fa2c82 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt @@ -25,6 +25,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper @@ -142,7 +143,7 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : } /** During the transition Secondary Activity shrinks to the bottom right corner. */ - @Presubmit + @FlakyTest(bugId = 315605409) @Test fun secondaryLayerShrinks() { flicker.assertLayers { |