diff options
554 files changed, 14333 insertions, 3505 deletions
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt index 6d1e6d0cbd73..4352c8ae982e 100644 --- a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt +++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt @@ -24,10 +24,11 @@ import android.content.pm.parsing.result.ParseTypeImpl import android.content.res.TypedArray import android.perftests.utils.BenchmarkState import android.perftests.utils.PerfStatusReporter +import android.util.ArraySet import androidx.test.filters.LargeTest +import com.android.internal.pm.parsing.pkg.PackageImpl +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.internal.util.ConcurrentUtils -import com.android.server.pm.parsing.pkg.PackageImpl -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import java.io.File import java.io.FileOutputStream import java.util.concurrent.ArrayBlockingQueue @@ -214,7 +215,10 @@ public class PackageParsingPerfTest { path, manifestArray, isCoreApp, + this, ) + override fun getHiddenApiWhitelistedApps() = ArraySet<String>() + override fun getInstallConstraintsAllowlist() = ArraySet<String>() }) override fun parseImpl(file: File) = diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index 03891bbec56a..e3ba50dc635b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -26,6 +26,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.SystemClock; import android.os.UserHandle; @@ -319,8 +320,8 @@ public final class BackgroundJobsController extends StateController { final boolean isStopped = mPackageManagerInternal.isPackageStopped(packageName, uid); mPackageStoppedState.add(uid, packageName, isStopped); return isStopped; - } catch (IllegalArgumentException e) { - Slog.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Couldn't determine stopped state for unknown package: " + packageName); return false; } } 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 83e3fabe475b..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 @@ -5285,9 +5286,10 @@ package android.app { field public static final int START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START = 21; // 0x15 field public static final int START_TIMESTAMP_RESERVED_RANGE_SYSTEM = 20; // 0x14 field public static final int START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE = 7; // 0x7 - field public static final int START_TYPE_COLD = 0; // 0x0 - field public static final int START_TYPE_HOT = 2; // 0x2 - field public static final int START_TYPE_WARM = 1; // 0x1 + field public static final int START_TYPE_COLD = 1; // 0x1 + field public static final int START_TYPE_HOT = 3; // 0x3 + field public static final int START_TYPE_UNSET = 0; // 0x0 + field public static final int START_TYPE_WARM = 2; // 0x2 } public final class AsyncNotedAppOp implements android.os.Parcelable { @@ -12605,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 @@ -13295,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 9f56933865db..802354e08653 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4091,7 +4091,7 @@ package android.content.pm { field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1 field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ANY_USER = 4194304; // 0x400000 - field @Deprecated public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000 + field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000 field @FlaggedApi("android.content.pm.fix_duplicated_flags") public static final long MATCH_CLONE_PROFILE_LONG = 17179869184L; // 0x400000000L field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000 @@ -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; } @@ -13051,7 +13052,7 @@ package android.service.voice { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.HotwordDetector.Callback); method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager(); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.VisualQueryDetector createVisualQueryDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VisualQueryDetector.Callback); - method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setIsReceiveSandboxedTrainingDataAllowed(boolean); + method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setShouldReceiveSandboxedTrainingData(boolean); } } @@ -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); @@ -16987,7 +16989,7 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback) throws android.telephony.satellite.SatelliteManager.SatelliteException; method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback); @@ -16999,7 +17001,7 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); 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/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8b39ed6fb411..6ddb36a72aa9 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4077,7 +4077,7 @@ public final class ActivityThread extends ClientTransactionHandler final LoadedApk sdkApk = getPackageInfo( contextInfo.getSdkApplicationInfo(), r.packageInfo.getCompatibilityInfo(), - ActivityContextInfo.CONTEXT_FLAGS); + contextInfo.getContextFlags()); final ContextImpl activityContext = ContextImpl.createActivityContext( this, sdkApk, r.activityInfo, r.token, displayId, r.overrideConfig); 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/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java index c8317c8faad5..656feb0401d6 100644 --- a/core/java/android/app/ApplicationStartInfo.java +++ b/core/java/android/app/ApplicationStartInfo.java @@ -104,14 +104,17 @@ public final class ApplicationStartInfo implements Parcelable { /** Process started due to Activity started for any reason not explicitly listed. */ public static final int START_REASON_START_ACTIVITY = 11; + /** Start type not yet set. */ + public static final int START_TYPE_UNSET = 0; + /** Process started from scratch. */ - public static final int START_TYPE_COLD = 0; + public static final int START_TYPE_COLD = 1; /** Process retained minimally SavedInstanceState. */ - public static final int START_TYPE_WARM = 1; + public static final int START_TYPE_WARM = 2; /** Process brought back to foreground. */ - public static final int START_TYPE_HOT = 2; + public static final int START_TYPE_HOT = 3; /** * Default. The system always creates a new instance of the activity in the target task and @@ -277,6 +280,7 @@ public final class ApplicationStartInfo implements Parcelable { @IntDef( prefix = {"START_TYPE_"}, value = { + START_TYPE_UNSET, START_TYPE_COLD, START_TYPE_WARM, START_TYPE_HOT, @@ -769,6 +773,7 @@ public final class ApplicationStartInfo implements Parcelable { private static String startTypeToString(@StartType int startType) { return switch (startType) { + case START_TYPE_UNSET -> "UNSET"; case START_TYPE_COLD -> "COLD"; case START_TYPE_WARM -> "WARM"; case START_TYPE_HOT -> "HOT"; diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index d93544972e7a..343348b89625 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -16,6 +16,8 @@ package android.app; +import static com.android.internal.util.Preconditions.checkArgument; + import android.annotation.DrawableRes; import android.annotation.FlaggedApi; import android.annotation.IntDef; @@ -390,7 +392,7 @@ public final class AutomaticZenRule implements Parcelable { */ @FlaggedApi(Flags.FLAG_MODES_API) public void setType(@Type int type) { - mType = type; + mType = checkValidType(type); } /** @@ -451,6 +453,24 @@ public final class AutomaticZenRule implements Parcelable { mAllowManualInvocation = allowManualInvocation; } + /** @hide */ + @FlaggedApi(Flags.FLAG_MODES_API) + public void validate() { + if (Flags.modesApi()) { + checkValidType(mType); + } + } + + @FlaggedApi(Flags.FLAG_MODES_API) + @Type + private static int checkValidType(@Type int type) { + checkArgument(type >= TYPE_UNKNOWN && type <= TYPE_MANAGED, + "Rule type must be one of TYPE_UNKNOWN, TYPE_OTHER, TYPE_SCHEDULE_TIME, " + + "TYPE_SCHEDULE_CALENDAR, TYPE_BEDTIME, TYPE_DRIVING, TYPE_IMMERSIVE, " + + "TYPE_THEATER, or TYPE_MANAGED"); + return type; + } + @Override public int describeContents() { return 0; @@ -625,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); } /** @@ -703,10 +723,10 @@ public final class AutomaticZenRule implements Parcelable { } /** - * Sets the type of the rule + * Sets the type of the rule. */ public @NonNull Builder setType(@Type int type) { - mType = type; + mType = checkValidType(type); return this; } @@ -714,7 +734,7 @@ public final class AutomaticZenRule implements Parcelable { * Sets a user visible description of when this rule will be active * (see {@link Condition#STATE_TRUE}). * - * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or + * <p>A description should be a (localized) string like "Mon-Fri, 9pm-7am" or * "When connected to [Car Name]". */ public @NonNull Builder setTriggerDescription(@Nullable String description) { 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/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 6e451479c5a4..4cf9fcab9092 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -16,6 +16,8 @@ package android.appwidget; +import static android.appwidget.flags.Flags.remoteAdapterConversion; + import android.annotation.BroadcastBehavior; import android.annotation.NonNull; import android.annotation.Nullable; @@ -566,11 +568,9 @@ public class AppWidgetManager { private void tryAdapterConversion( FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action, RemoteViews original, String failureMsg) { - final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() + if (remoteAdapterConversion() && (mHasPostedLegacyLists = mHasPostedLegacyLists - || (original != null && original.hasLegacyLists())); - - if (isConvertingAdapter) { + || (original != null && original.hasLegacyLists()))) { final RemoteViews viewsCopy = new RemoteViews(original); Runnable updateWidgetWithTask = () -> { try { @@ -587,13 +587,12 @@ public class AppWidgetManager { } updateWidgetWithTask.run(); - return; - } - - try { - action.acceptOrThrow(original); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); + } else { + try { + action.acceptOrThrow(original); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } } @@ -838,22 +837,20 @@ public class AppWidgetManager { return; } - if (!RemoteViews.isAdapterConversionEnabled()) { + if (remoteAdapterConversion()) { + if (Looper.myLooper() == Looper.getMainLooper()) { + mHasPostedLegacyLists = true; + createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange( + appWidgetIds, viewId)); + } else { + notifyCollectionWidgetChange(appWidgetIds, viewId); + } + } else { try { mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } - - return; - } - - if (Looper.myLooper() == Looper.getMainLooper()) { - mHasPostedLegacyLists = true; - createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(appWidgetIds, - viewId)); - } else { - notifyCollectionWidgetChange(appWidgetIds, viewId); } } diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig index 6a735a418b58..c95b864c08bb 100644 --- a/core/java/android/appwidget/flags.aconfig +++ b/core/java/android/appwidget/flags.aconfig @@ -6,3 +6,10 @@ flag { description: "Enable support for generated previews in AppWidgetManager" bug: "306546610" } + +flag { + name: "remote_adapter_conversion" + namespace: "app_widgets" + description: "Enable adapter conversion to RemoteCollectionItemsAdapter" + bug: "245950570" +}
\ No newline at end of file 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 e9f419e9a8ce..a97de6368b8c 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -64,6 +64,9 @@ interface ILauncherApps { PendingIntent getActivityLaunchIntent(String callingPackage, in ComponentName component, 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 0cd4358b2c91..1d2b1aff46bc 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -799,6 +799,78 @@ 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. + * + * <p>An empty list denotes that all system packages are installed for that user at creation. + * This behaviour is inherited from the underlining UserManager API. + * + * @param userHandle the user for which installed system packages are required. + * @return {@link List} of {@link String}, representing the package name of the installed + * package. Can be empty but not null. + * @hide + */ + @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) + public List<String> getPreInstalledSystemPackages(@NonNull UserHandle userHandle) { + if (DEBUG) { + Log.i(TAG, "getPreInstalledSystemPackages for user: " + userHandle); + } + try { + return mService.getPreInstalledSystemPackages(userHandle); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + /** * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it * returns null. diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 5dee65b62201..4f61613b9c6e 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -231,7 +231,7 @@ public class PackageInfo implements Parcelable { * or null if there were none. This is only filled in if the flag * {@link PackageManager#GET_PERMISSIONS} was set. Each value matches * the corresponding entry in {@link #requestedPermissions}, and will have - * the flags {@link #REQUESTED_PERMISSION_GRANTED} and + * the flags {@link #REQUESTED_PERMISSION_GRANTED}, {@link #REQUESTED_PERMISSION_IMPLICIT}, and * {@link #REQUESTED_PERMISSION_NEVER_FOR_LOCATION} set as appropriate. */ @Nullable 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 4d75a5147d6e..a8638708824b 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1225,12 +1225,10 @@ public abstract class PackageManager { public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO; /** - * @deprecated Use {@link #MATCH_CLONE_PROFILE_LONG} instead. + * Use {@link #MATCH_CLONE_PROFILE_LONG} instead. * * @hide */ - @SuppressLint("UnflaggedApi") // Just adding the @Deprecated annotation - @Deprecated @SystemApi public static final int MATCH_CLONE_PROFILE = 0x20000000; @@ -1519,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 {} @@ -1766,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. * @@ -9992,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/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 93fbe8aee8d4..7cf10d89004f 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1216,7 +1216,7 @@ public abstract class CameraMetadata<TKey> { * <ul> * <li>Profile {@link android.hardware.camera2.params.DynamicRangeProfiles#HLG10 }</li> * <li>All mandatory stream combinations for this specific capability as per - * <a href="CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li> + * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li> * <li>In case the device is not able to capture some combination of supported * standard 8-bit and/or 10-bit dynamic range profiles within the same capture request, * then those constraints must be listed in diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 06397c9a1598..ded96a23e11e 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1484,7 +1484,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>To start a CaptureSession with a target FPS range different from the * capture request template's default value, the application * is strongly recommended to call - * {@link SessionConfiguration#setSessionParameters } + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters } * with the target fps range before creating the capture session. The aeTargetFpsRange is * typically a session parameter. Specifying it at session creation time helps avoid * session reconfiguration delays in cases like 60fps or high speed recording.</p> @@ -2161,7 +2161,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * OFF if the recording output is not stabilized, or if there are no output * Surface types that can be stabilized.</p> * <p>The application is strongly recommended to call - * {@link SessionConfiguration#setSessionParameters } + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters } * with the desired video stabilization mode before creating the capture session. * Video stabilization mode is a session parameter on many devices. Specifying * it at session creation time helps avoid reconfiguration delay caused by difference diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index ab4406c37c8e..1d26d69a58b9 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -899,7 +899,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>To start a CaptureSession with a target FPS range different from the * capture request template's default value, the application * is strongly recommended to call - * {@link SessionConfiguration#setSessionParameters } + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters } * with the target fps range before creating the capture session. The aeTargetFpsRange is * typically a session parameter. Specifying it at session creation time helps avoid * session reconfiguration delays in cases like 60fps or high speed recording.</p> @@ -2382,7 +2382,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * OFF if the recording output is not stabilized, or if there are no output * Surface types that can be stabilized.</p> * <p>The application is strongly recommended to call - * {@link SessionConfiguration#setSessionParameters } + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters } * with the desired video stabilization mode before creating the capture session. * Video stabilization mode is a session parameter on many devices. Specifying * it at session creation time helps avoid reconfiguration delay caused by difference 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/os/Parcel.java b/core/java/android/os/Parcel.java index f2930fe45295..8e860c35388d 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -465,9 +465,7 @@ public final class Parcel { private static native byte[] nativeMarshall(long nativePtr); private static native void nativeUnmarshall( long nativePtr, byte[] data, int offset, int length); - @RavenwoodThrow private static native int nativeCompareData(long thisNativePtr, long otherNativePtr); - @RavenwoodThrow private static native boolean nativeCompareDataInRange( long ptrA, int offsetA, long ptrB, int offsetB, int length); private static native void nativeAppendFrom( diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 145981c92283..83d237d6e53b 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -78,6 +78,13 @@ flag { } flag { + name: "adpf_use_fmq_channel" + namespace: "game" + description: "Guards use of the FMQ channel for ADPF" + bug: "315894228" +} + +flag { name: "battery_service_support_current_adb_command" namespace: "backstage_power" description: "Whether or not BatteryService supports adb commands for Current values." diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java index 531626b1e85f..6e771f8f0ffe 100644 --- a/core/java/android/service/notification/Condition.java +++ b/core/java/android/service/notification/Condition.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2014, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,8 @@ package android.service.notification; +import static com.android.internal.util.Preconditions.checkArgument; + import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -117,7 +119,7 @@ public final class Condition implements Parcelable { /** The source of, or reason for, the state change represented by this Condition. **/ @FlaggedApi(Flags.FLAG_MODES_API) - public final @Source int source; + public final @Source int source; // default = SOURCE_UNKNOWN /** * The maximum string length for any string contained in this condition. @@ -179,7 +181,7 @@ public final class Condition implements Parcelable { this.line2 = getTrimmedString(line2); this.icon = icon; this.state = state; - this.source = source; + this.source = checkValidSource(source); this.flags = flags; } @@ -197,10 +199,26 @@ public final class Condition implements Parcelable { source.readInt()); } + /** @hide */ + public void validate() { + if (Flags.modesApi()) { + checkValidSource(source); + } + } + private static boolean isValidState(int state) { return state >= STATE_FALSE && state <= STATE_ERROR; } + private static int checkValidSource(@Source int source) { + if (Flags.modesApi()) { + checkArgument(source >= SOURCE_UNKNOWN && source <= SOURCE_CONTEXT, + "Condition source must be one of SOURCE_UNKNOWN, SOURCE_USER_ACTION, " + + "SOURCE_SCHEDULE, or SOURCE_CONTEXT"); + } + return source; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(id, 0); diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index a5b087c05dfa..fcdc5fe71e4e 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -1980,6 +1980,7 @@ public class ZenModeConfig implements Parcelable { @Nullable public ZenDeviceEffects zenDeviceEffects; public boolean modified; // rule has been modified from initial creation public String pkg; + @AutomaticZenRule.Type public int type = AutomaticZenRule.TYPE_UNKNOWN; public String triggerDescription; public String iconResName; diff --git a/core/java/android/service/persistentdata/OWNERS b/core/java/android/service/persistentdata/OWNERS new file mode 100644 index 000000000000..6dfb888dedad --- /dev/null +++ b/core/java/android/service/persistentdata/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/pdb/OWNERS 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/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index fba09233e4f4..75ab48a43da6 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -1042,13 +1042,13 @@ public class VoiceInteractionService extends Service { @SystemApi @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS) @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION) - public void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed) { - Log.i(TAG, "setIsReceiveSandboxedTrainingDataAllowed to " + allowed); + public void setShouldReceiveSandboxedTrainingData(boolean allowed) { + Log.i(TAG, "setShouldReceiveSandboxedTrainingData to " + allowed); if (mSystemService == null) { throw new IllegalStateException("Not available until onReady() is called"); } try { - mSystemService.setIsReceiveSandboxedTrainingDataAllowed(allowed); + mSystemService.setShouldReceiveSandboxedTrainingData(allowed); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } 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/ISurfaceControlViewHostParent.aidl b/core/java/android/view/ISurfaceControlViewHostParent.aidl index f42e00148587..559c20ee4825 100644 --- a/core/java/android/view/ISurfaceControlViewHostParent.aidl +++ b/core/java/android/view/ISurfaceControlViewHostParent.aidl @@ -16,6 +16,7 @@ package android.view; +import android.view.KeyEvent; import android.view.WindowManager; /** @@ -24,4 +25,6 @@ import android.view.WindowManager; */ oneway interface ISurfaceControlViewHostParent { void updateParams(in WindowManager.LayoutParams[] childAttrs); + // To forward the back key event from embedded to host app. + void forwardBackKeyToParent(in KeyEvent keyEvent); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index dbacca5def51..9bf43a390d70 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -162,6 +162,8 @@ interface IWindowSession { * @param flags See {@code View#startDragAndDrop} * @param surface Surface containing drag shadow image * @param touchSource See {@code InputDevice#getSource()} + * @param touchDeviceId device ID of last touch event + * @param pointerId pointer ID of last touch event * @param touchX X coordinate of last touch point * @param touchY Y coordinate of last touch point * @param thumbCenterX X coordinate for the position within the shadow image that should be @@ -171,9 +173,9 @@ interface IWindowSession { * @param data Data transferred by drag and drop * @return Token of drag operation which will be passed to cancelDragAndDrop. */ - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) IBinder performDrag(IWindow window, int flags, in SurfaceControl surface, int touchSource, - float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data); + int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX, + float thumbCenterY, in ClipData data); /** * Drops the content of the current drag operation for accessibility diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 405653123f79..4840f003da3e 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -447,6 +447,7 @@ public class SurfaceControlViewHost { addWindowToken(attrs); view.setLayoutParams(attrs); mViewRoot.setView(view, attrs, null); + mViewRoot.setBackKeyCallbackForWindowlessWindow(mWm::forwardBackKeyToParent); } /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index a44a95a1677f..108de281a411 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -37,6 +37,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; import android.graphics.RenderNode; +import android.hardware.input.InputManager; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -159,6 +160,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private static final boolean DEBUG = false; private static final boolean DEBUG_POSITION = false; + private static final long FORWARD_BACK_KEY_TOLERANCE_MS = 100; + @UnsupportedAppUsage( maxTargetSdk = Build.VERSION_CODES.TIRAMISU, publicAlternatives = "Track {@link SurfaceHolder#addCallback} instead") @@ -326,6 +329,41 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall }); } } + + @Override + public void forwardBackKeyToParent(@NonNull KeyEvent keyEvent) { + runOnUiThread(() -> { + if (!isAttachedToWindow() || keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK) { + return; + } + final ViewRootImpl vri = getViewRootImpl(); + if (vri == null) { + return; + } + final InputManager inputManager = mContext.getSystemService(InputManager.class); + if (inputManager == null) { + return; + } + // Check that the event was created recently. + final long timeDiff = SystemClock.uptimeMillis() - keyEvent.getEventTime(); + if (timeDiff > FORWARD_BACK_KEY_TOLERANCE_MS) { + Log.e(TAG, "Ignore the input event that exceed the tolerance time, " + + "exceed " + timeDiff + "ms"); + return; + } + if (inputManager.verifyInputEvent(keyEvent) == null) { + Log.e(TAG, "Received invalid input event"); + return; + } + try { + vri.processingBackKey(true); + vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */, + true /* processImmediately */); + } finally { + vri.processingBackKey(false); + } + }); + } }; private final boolean mRtDrivenClipping = Flags.clipSurfaceviews(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 75f8eba01fa2..bb5ee0359b6b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -28340,6 +28340,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, IBinder token = mAttachInfo.mSession.performDrag( mAttachInfo.mWindow, flags, null, mAttachInfo.mViewRootImpl.getLastTouchSource(), + mAttachInfo.mViewRootImpl.getLastTouchDeviceId(), + mAttachInfo.mViewRootImpl.getLastTouchPointerId(), 0f, 0f, 0f, 0f, data); if (ViewDebug.DEBUG_DRAG) { Log.d(VIEW_LOG_TAG, "startDragAndDrop via a11y action returned " + token); @@ -28414,7 +28416,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl, - root.getLastTouchSource(), lastTouchPoint.x, lastTouchPoint.y, + root.getLastTouchSource(), root.getLastTouchDeviceId(), + root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y, shadowTouchPoint.x, shadowTouchPoint.y, data); if (ViewDebug.DEBUG_DRAG) { Log.d(VIEW_LOG_TAG, "performDrag returned " + token); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5cbb42e0e346..e83488e2689e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -76,13 +76,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_E import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -96,6 +91,7 @@ import static android.view.accessibility.Flags.reduceWindowContentChangedEventTh import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER; import static android.view.flags.Flags.toolkitSetFrameRateReadOnly; +import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision; import static com.android.input.flags.Flags.enablePointerChoreographer; @@ -255,7 +251,7 @@ import java.util.OptionalInt; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; -import java.util.function.Consumer; +import java.util.function.Predicate; /** * The top of a view hierarchy, implementing the needed protocol between View @@ -375,6 +371,8 @@ public final class ViewRootImpl implements ViewParent, */ private static final int KEEP_CLEAR_AREA_REPORT_RATE_MILLIS = 100; + private static final long NANOS_PER_SEC = 1000000000; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); @@ -620,6 +618,13 @@ public final class ViewRootImpl implements ViewParent, boolean mUpcomingWindowFocus; @GuardedBy("this") boolean mUpcomingInTouchMode; + // While set, allow this VRI to handle back key without drop it. + private boolean mProcessingBackKey; + /** + * Compatibility {@link OnBackInvokedCallback} for windowless window, to forward the back + * key event host app. + */ + private Predicate<KeyEvent> mWindowlessBackKeyCallback; public boolean mTraversalScheduled; int mTraversalBarrier; @@ -808,6 +813,8 @@ public final class ViewRootImpl implements ViewParent, final PointF mDragPoint = new PointF(); final PointF mLastTouchPoint = new PointF(); int mLastTouchSource; + int mLastTouchDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; + int mLastTouchPointerId; /** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/ private int mLastClickToolType; @@ -822,6 +829,8 @@ public final class ViewRootImpl implements ViewParent, private boolean mInsetsAnimationRunning; + private long mPreviousFrameDrawnTime = -1; + /** * The resolved pointer icon type requested by this window. * A null value indicates the resolved pointer icon has not yet been calculated. @@ -1059,11 +1068,14 @@ public final class ViewRootImpl implements ViewParent, private boolean mChildBoundingInsetsChanged = false; private String mTag = TAG; + private String mFpsTraceName; private static boolean sToolkitSetFrameRateReadOnlyFlagValue; + private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; static { sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly(); + sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision(); } // The latest input event from the gesture that was used to resolve the pointer icon. @@ -1307,6 +1319,7 @@ public final class ViewRootImpl implements ViewParent, attrs = mWindowAttributes; setTag(); + mFpsTraceName = "FPS of " + getTitle(); if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0 @@ -3194,7 +3207,11 @@ public final class ViewRootImpl implements ViewParent, host.dispatchAttachedToWindow(mAttachInfo, 0); mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true); dispatchApplyInsets(host); - if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) { + if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled() + // Don't register compat OnBackInvokedCallback for windowless window. + // The onBackInvoked event by default should forward to host app, so the + // host app can decide the behavior. + && mWindowlessBackKeyCallback == null) { // For apps requesting legacy back behavior, we add a compat callback that // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views. // This way from system point of view, these apps are providing custom @@ -4726,6 +4743,31 @@ public final class ViewRootImpl implements ViewParent, } } + /** + * Called from draw() to collect metrics for frame rate decision. + */ + private void collectFrameRateDecisionMetrics() { + if (!Trace.isEnabled()) { + if (mPreviousFrameDrawnTime > 0) mPreviousFrameDrawnTime = -1; + return; + } + + if (mPreviousFrameDrawnTime < 0) { + mPreviousFrameDrawnTime = mChoreographer.getExpectedPresentationTimeNanos(); + return; + } + + long expectedDrawnTime = mChoreographer.getExpectedPresentationTimeNanos(); + long timeDiff = expectedDrawnTime - mPreviousFrameDrawnTime; + if (timeDiff <= 0) { + return; + } + + long fps = NANOS_PER_SEC / timeDiff; + Trace.setCounter(mFpsTraceName, fps); + mPreviousFrameDrawnTime = expectedDrawnTime; + } + private void reportDrawFinished(@Nullable Transaction t, int seqId) { if (DEBUG_BLAST) { Log.d(mTag, "reportDrawFinished"); @@ -5044,6 +5086,9 @@ public final class ViewRootImpl implements ViewParent, if (DEBUG_FPS) { trackFPS(); } + if (sToolkitMetricsForFrameRateDecisionFlagValue) { + collectFrameRateDecisionMetrics(); + } if (!sFirstDrawComplete) { synchronized (sFirstDrawHandlers) { @@ -6660,7 +6705,8 @@ public final class ViewRootImpl implements ViewParent, // Find a reason for dropping or canceling the event. final String reason; - if (!mAttachInfo.mHasWindowFocus + // The embedded window is focused, allow this VRI to handle back key. + if (!mAttachInfo.mHasWindowFocus && !(mProcessingBackKey && isBack(q.mEvent)) && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) && !isAutofillUiShowing()) { // This is a non-pointer event and the window doesn't currently have input focus @@ -6883,10 +6929,20 @@ public final class ViewRootImpl implements ViewParent, // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}. - if (isBack(keyEvent) - && mContext != null - && mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) { - return doOnBackKeyEvent(keyEvent); + if (isBack(keyEvent)) { + if (mWindowlessBackKeyCallback != null) { + if (mWindowlessBackKeyCallback.test(keyEvent)) { + return keyEvent.getAction() == KeyEvent.ACTION_UP + && !keyEvent.isCanceled() + ? FINISH_HANDLED : FINISH_NOT_HANDLED; + } else { + // Unable to forward the back key to host, forward to next stage. + return FORWARD; + } + } else if (mContext != null + && mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) { + return doOnBackKeyEvent(keyEvent); + } } if (mInputQueue != null) { @@ -7109,6 +7165,8 @@ public final class ViewRootImpl implements ViewParent, mLastTouchPoint.x = event.getRawX(); mLastTouchPoint.y = event.getRawY(); mLastTouchSource = event.getSource(); + mLastTouchDeviceId = event.getDeviceId(); + mLastTouchPointerId = event.getPointerId(0); // Register last ACTION_UP. This will be propagated to IME. if (event.getActionMasked() == MotionEvent.ACTION_UP) { @@ -8520,6 +8578,14 @@ public final class ViewRootImpl implements ViewParent, return mLastTouchSource; } + public int getLastTouchDeviceId() { + return mLastTouchDeviceId; + } + + public int getLastTouchPointerId() { + return mLastTouchPointerId; + } + /** * Used by InputMethodManager. * @hide @@ -10529,6 +10595,11 @@ public final class ViewRootImpl implements ViewParent, mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget(); } + // Make this VRI able to process back key without drop it. + void processingBackKey(boolean processing) { + mProcessingBackKey = processing; + } + /** * Collect and include any ScrollCaptureCallback instances registered with the window. * @@ -11753,13 +11824,18 @@ public final class ViewRootImpl implements ViewParent, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); - enqueueInputEvent(ev); + enqueueInputEvent(ev, null /* receiver */, 0 /* flags */, true /* processImmediately */); } private void registerCompatOnBackInvokedCallback() { mCompatOnBackInvokedCallback = () -> { - sendBackKeyEvent(KeyEvent.ACTION_DOWN); - sendBackKeyEvent(KeyEvent.ACTION_UP); + try { + processingBackKey(true); + sendBackKeyEvent(KeyEvent.ACTION_DOWN); + sendBackKeyEvent(KeyEvent.ACTION_UP); + } finally { + processingBackKey(false); + } }; if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) { Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher"); @@ -12097,11 +12173,9 @@ public final class ViewRootImpl implements ViewParent, boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN || motionEventAction == MotionEvent.ACTION_MOVE || motionEventAction == MotionEvent.ACTION_UP; - boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION - || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION - || windowType == TYPE_NOTIFICATION_SHADE || windowType == TYPE_STATUS_BAR; + boolean undesiredType = windowType == TYPE_INPUT_METHOD; // use toolkitSetFrameRate flag to gate the change - return desiredAction && desiredType && sToolkitSetFrameRateReadOnlyFlagValue; + return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue; } /** @@ -12196,4 +12270,13 @@ public final class ViewRootImpl implements ViewParent, } return false; } + + /** + * Set the default back key callback for windowless window, to forward the back key event + * to host app. + * MUST NOT call this method for normal window. + */ + void setBackKeyCallbackForWindowlessWindow(@NonNull Predicate<KeyEvent> callback) { + mWindowlessBackKeyCallback = callback; + } } 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/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index d817e6f51f55..d6ac56239aed 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.content.res.Configuration; @@ -488,8 +489,9 @@ public class WindowlessWindowManager implements IWindowSession { @Override public android.os.IBinder performDrag(android.view.IWindow window, int flags, - android.view.SurfaceControl surface, int touchSource, float touchX, float touchY, - float thumbCenterX, float thumbCenterY, android.content.ClipData data) { + android.view.SurfaceControl surface, int touchSource, int touchDeviceId, + int touchPointerId, float touchX, float touchY, float thumbCenterX, float thumbCenterY, + android.content.ClipData data) { return null; } @@ -703,4 +705,17 @@ public class WindowlessWindowManager implements IWindowSession { } } } + + boolean forwardBackKeyToParent(@NonNull KeyEvent keyEvent) { + if (mParentInterface == null) { + return false; + } + try { + mParentInterface.forwardBackKeyToParent(keyEvent); + } catch (RemoteException e) { + Log.e(TAG, "Failed to forward back key To Parent: ", e); + return false; + } + return true; + } } diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig index a467afe5d06a..0aa516e08697 100644 --- a/core/java/android/view/flags/refresh_rate_flags.aconfig +++ b/core/java/android/view/flags/refresh_rate_flags.aconfig @@ -42,4 +42,12 @@ flag { namespace: "core_graphics" description: "Enable the `setFrameRate` callback" bug: "299946220" +} + +flag { + name: "toolkit_metrics_for_frame_rate_decision" + namespace: "toolkit" + description: "Feature flag for toolkit metrics collecting for frame rate decision" + bug: "301343249" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index da3134856731..8ad10af7250a 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -16,6 +16,7 @@ package android.widget; +import static android.appwidget.flags.Flags.remoteAdapterConversion; import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR; import android.annotation.AttrRes; @@ -36,7 +37,6 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityOptions; import android.app.ActivityThread; -import android.app.AppGlobals; import android.app.Application; import android.app.LoadedApk; import android.app.PendingIntent; @@ -108,7 +108,6 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.CompoundButton.OnCheckedChangeListener; import com.android.internal.R; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.util.Preconditions; import com.android.internal.widget.IRemoteViewsFactory; @@ -4950,21 +4949,11 @@ public class RemoteViews implements Parcelable, Filter { */ @Deprecated public void setRemoteAdapter(@IdRes int viewId, Intent intent) { - if (isAdapterConversionEnabled()) { + if (remoteAdapterConversion()) { addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent)); - return; + } else { + addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); } - addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); - } - - /** - * @hide - * @return True if the remote adapter conversion is enabled - */ - public static boolean isAdapterConversionEnabled() { - return AppGlobals.getIntCoreSetting( - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION, - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) != 0; } /** diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 82ee8fc47571..e92c6a6c4b34 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -397,5 +397,5 @@ interface IVoiceInteractionManagerService { * sandboxed detection (from trusted process). */ @EnforcePermission("MANAGE_HOTWORD_DETECTION") - void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed); + void setShouldReceiveSandboxedTrainingData(boolean allowed); } 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/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index e494346bae5c..bd806bfb3e24 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -519,24 +519,6 @@ public final class SystemUiDeviceConfigFlags { public static final String TASK_MANAGER_SHOW_FOOTER_DOT = "task_manager_show_footer_dot"; /** - * (boolean) Whether to enable the adapter conversion in RemoteViews - */ - public static final String REMOTEVIEWS_ADAPTER_CONVERSION = - "CursorControlFeature__remoteviews_adapter_conversion"; - - /** - * The key name used in app core settings for {@link #REMOTEVIEWS_ADAPTER_CONVERSION} - */ - public static final String KEY_REMOTEVIEWS_ADAPTER_CONVERSION = - "systemui__remoteviews_adapter_conversion"; - - /** - * Default value for whether the adapter conversion is enabled or not. This is set for - * RemoteViews and should not be a common practice. - */ - public static final boolean REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT = false; - - /** * (boolean) Whether the task manager should show a stop button if the app is allowlisted * by the user. */ diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java index f460233f0edd..96740c59ec06 100644 --- a/core/java/com/android/internal/jank/Cuj.java +++ b/core/java/com/android/internal/jank/Cuj.java @@ -109,6 +109,7 @@ public class Cuj { * eg: Exit the app using back gesture. */ public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78; + // 79 is reserved. public static final int CUJ_IME_INSETS_SHOW_ANIMATION = 80; public static final int CUJ_IME_INSETS_HIDE_ANIMATION = 81; @@ -119,10 +120,11 @@ public class Cuj { public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84; public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85; public static final int CUJ_PREDICTIVE_BACK_HOME = 86; + public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87; // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE. @VisibleForTesting - static final int LAST_CUJ = CUJ_PREDICTIVE_BACK_HOME; + static final int LAST_CUJ = CUJ_LAUNCHER_SEARCH_QSB_OPEN; /** @hide */ @IntDef({ @@ -204,6 +206,7 @@ public class Cuj { CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY, CUJ_PREDICTIVE_BACK_CROSS_TASK, CUJ_PREDICTIVE_BACK_HOME, + CUJ_LAUNCHER_SEARCH_QSB_OPEN, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -295,6 +298,8 @@ public class Cuj { CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = + FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN; } private Cuj() { @@ -467,6 +472,8 @@ public class Cuj { return "PREDICTIVE_BACK_CROSS_TASK"; case CUJ_PREDICTIVE_BACK_HOME: return "PREDICTIVE_BACK_HOME"; + case CUJ_LAUNCHER_SEARCH_QSB_OPEN: + return "LAUNCHER_SEARCH_QSB_OPEN"; } return "UNKNOWN"; } 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/AppInfoUtils.java b/core/java/com/android/internal/pm/parsing/AppInfoUtils.java new file mode 100644 index 000000000000..38a2fe2a77a1 --- /dev/null +++ b/core/java/com/android/internal/pm/parsing/AppInfoUtils.java @@ -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.internal.pm.parsing; + +import android.annotation.CheckResult; +import android.content.pm.ApplicationInfo; + +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.server.pm.pkg.AndroidPackage; + +public class AppInfoUtils { + + /** + * @see ApplicationInfo#flags + */ + public static int appInfoFlags(AndroidPackage pkg) { + // @formatter:off + int pkgWithoutStateFlags = flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE) + | flag(pkg.isHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED) + | flag(pkg.isBackupAllowed(), ApplicationInfo.FLAG_ALLOW_BACKUP) + | flag(pkg.isKillAfterRestoreAllowed(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE) + | flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION) + | flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY) + | flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT) + | flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE) + | flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE) + | flag(pkg.isDeclaredHavingCode(), ApplicationInfo.FLAG_HAS_CODE) + | flag(pkg.isTaskReparentingAllowed(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) + | flag(pkg.isClearUserDataAllowed(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) + | flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP) + | flag(pkg.isCleartextTrafficAllowed(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) + | flag(pkg.isRtlSupported(), ApplicationInfo.FLAG_SUPPORTS_RTL) + | flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY) + | flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH) + | flag(pkg.isExtractNativeLibrariesRequested(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) + | flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME) + | flag(pkg.isSmallScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) + | flag(pkg.isNormalScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) + | flag(pkg.isLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) + | flag(pkg.isExtraLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) + | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) + | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) + | flag(AndroidPackageLegacyUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM) + | flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST); + + return pkgWithoutStateFlags; + // @formatter:on + } + + /** @see ApplicationInfo#privateFlags */ + public static int appInfoPrivateFlags(AndroidPackage pkg) { + // @formatter:off + int pkgWithoutStateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) + | flag(pkg.isResourceOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY) + | flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) + | flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) + | flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) + | flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND) + | flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX) + | flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) + | flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) + | flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) + | flag(pkg.isClearUserDataOnFailedRestoreAllowed(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE) + | flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) + | flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) + | flag(pkg.isNonSdkApiRequested(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API) + | flag(pkg.isUserDataFragile(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA) + | flag(pkg.isSaveStateDisallowed(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) + | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) + | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING) + | flag(AndroidPackageLegacyUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) + | flag(AndroidPackageLegacyUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) + | flag(AndroidPackageLegacyUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM) + | flag(AndroidPackageLegacyUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR) + | flag(AndroidPackageLegacyUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT) + | flag(AndroidPackageLegacyUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM) + | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY); + + Boolean resizeableActivity = pkg.getResizeableActivity(); + if (resizeableActivity != null) { + if (resizeableActivity) { + pkgWithoutStateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE; + } else { + pkgWithoutStateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE; + } + } + + return pkgWithoutStateFlags; + // @formatter:on + } + + + /** @see ApplicationInfo#privateFlagsExt */ + public static int appInfoPrivateFlagsExt(AndroidPackage pkg, + boolean isAllowlistedForHiddenApis) { + // @formatter:off + int pkgWithoutStateFlags = flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE) + | flag(pkg.hasRequestForegroundServiceExemption(), ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION) + | flag(pkg.isAttributionsUserVisible(), ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE) + | flag(pkg.isOnBackInvokedCallbackEnabled(), ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK) + | flag(isAllowlistedForHiddenApis, ApplicationInfo.PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS); + return pkgWithoutStateFlags; + // @formatter:on + } + + @CheckResult + private static int flag(boolean hasFlag, int flag) { + return hasFlag ? flag : 0; + } +} diff --git a/core/java/com/android/internal/pm/parsing/PackageParserException.java b/core/java/com/android/internal/pm/parsing/PackageParserException.java new file mode 100644 index 000000000000..4250bbd9baf6 --- /dev/null +++ b/core/java/com/android/internal/pm/parsing/PackageParserException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.pm.parsing; + +public class PackageParserException extends Exception { + public final int error; + + public PackageParserException(int error, String detailMessage) { + super(detailMessage); + this.error = error; + } + + public PackageParserException(int error, String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + this.error = error; + } +}
\ No newline at end of file diff --git a/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java new file mode 100644 index 000000000000..e65f1c960b6a --- /dev/null +++ b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.pm.parsing.pkg; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; + +import com.android.internal.pm.pkg.parsing.ParsingPackageHidden; +import com.android.server.pm.pkg.AndroidPackage; + +/** @hide */ +public class AndroidPackageLegacyUtils { + + private AndroidPackageLegacyUtils() { + } + + /** + * Returns the primary ABI as parsed from the package. Used only during parsing and derivation. + * Otherwise prefer {@link PackageState#getPrimaryCpuAbi()}. + */ + public static String getRawPrimaryCpuAbi(AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).getPrimaryCpuAbi(); + } + + /** + * Returns the secondary ABI as parsed from the package. Used only during parsing and + * derivation. Otherwise prefer {@link PackageState#getSecondaryCpuAbi()}. + */ + public static String getRawSecondaryCpuAbi(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).getSecondaryCpuAbi(); + } + + @Deprecated + @NonNull + public static ApplicationInfo generateAppInfoWithoutState(AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).toAppInfoWithoutState(); + } + + /** + * Replacement of unnecessary legacy getRealPackage. Only returns a value if the package was + * actually renamed. + */ + @Nullable + public static String getRealPackageOrNull(@NonNull AndroidPackage pkg, boolean isSystem) { + if (pkg.getOriginalPackages().isEmpty() || !isSystem) { + return null; + } + + return pkg.getManifestPackageName(); + } + + public static void fillVersionCodes(@NonNull AndroidPackage pkg, @NonNull PackageInfo info) { + info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode(); + info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor(); + } + + /** + * @deprecated Use {@link PackageState#isSystem} + */ + @Deprecated + public static boolean isSystem(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isSystem(); + } + + /** + * @deprecated Use {@link PackageState#isSystemExt} + */ + @Deprecated + public static boolean isSystemExt(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isSystemExt(); + } + + /** + * @deprecated Use {@link PackageState#isPrivileged} + */ + @Deprecated + public static boolean isPrivileged(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isPrivileged(); + } + + /** + * @deprecated Use {@link PackageState#isOem} + */ + @Deprecated + public static boolean isOem(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isOem(); + } + + /** + * @deprecated Use {@link PackageState#isVendor} + */ + @Deprecated + public static boolean isVendor(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isVendor(); + } + + /** + * @deprecated Use {@link PackageState#isProduct} + */ + @Deprecated + public static boolean isProduct(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isProduct(); + } + + /** + * @deprecated Use {@link PackageState#isOdm} + */ + @Deprecated + public static boolean isOdm(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isOdm(); + } +} diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java index da58d47edbfe..83acc47d637f 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java +++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.parsing.pkg; +package com.android.internal.pm.parsing.pkg; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; @@ -50,47 +50,44 @@ import android.util.SparseIntArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.parsing.pkg.AndroidPackageHidden; -import com.android.internal.pm.parsing.pkg.AndroidPackageInternal; -import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.parsing.AppInfoUtils; import com.android.internal.pm.pkg.AndroidPackageSplitImpl; +import com.android.internal.pm.pkg.SEInfoUtil; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedActivityImpl; import com.android.internal.pm.pkg.component.ParsedApexSystemService; +import com.android.internal.pm.pkg.component.ParsedApexSystemServiceImpl; import com.android.internal.pm.pkg.component.ParsedAttribution; +import com.android.internal.pm.pkg.component.ParsedAttributionImpl; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedInstrumentation; +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl; +import com.android.internal.pm.pkg.component.ParsedPermissionImpl; import com.android.internal.pm.pkg.component.ParsedProcess; +import com.android.internal.pm.pkg.component.ParsedProcessImpl; import com.android.internal.pm.pkg.component.ParsedProvider; +import com.android.internal.pm.pkg.component.ParsedProviderImpl; import com.android.internal.pm.pkg.component.ParsedService; +import com.android.internal.pm.pkg.component.ParsedServiceImpl; import com.android.internal.pm.pkg.component.ParsedUsesPermission; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.internal.pm.pkg.parsing.ParsingPackageHidden; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; -import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.AndroidPackageSplit; -import com.android.server.pm.pkg.SELinuxUtil; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ParsedActivityImpl; -import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl; -import com.android.server.pm.pkg.component.ParsedAttributionImpl; -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl; -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl; -import com.android.server.pm.pkg.component.ParsedPermissionImpl; -import com.android.server.pm.pkg.component.ParsedProcessImpl; -import com.android.server.pm.pkg.component.ParsedProviderImpl; -import com.android.server.pm.pkg.component.ParsedServiceImpl; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import libcore.util.EmptyArray; @@ -422,8 +419,10 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, @NonNull public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath, - @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) { - return new PackageImpl(packageName, baseCodePath, codePath, manifestArray, isCoreApp); + @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp, + @Nullable ParsingPackageUtils.Callback callback) { + return new PackageImpl( + packageName, baseCodePath, codePath, manifestArray, isCoreApp, callback); } /** @@ -453,7 +452,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, @NonNull @VisibleForTesting public static ParsingPackage forTesting(String packageName, String baseCodePath) { - return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false); + return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false, null); } @NonNull @@ -727,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) { @@ -2694,12 +2678,16 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, private String mBaseAppDataCredentialProtectedDirForSystemUser; private String mBaseAppDataDeviceProtectedDirForSystemUser; + ParsingPackageUtils.Callback mCallback; + @VisibleForTesting public PackageImpl(@NonNull String packageName, @NonNull String baseApkPath, - @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp) { + @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp, + @Nullable ParsingPackageUtils.Callback callback) { this.packageName = TextUtils.safeIntern(packageName); this.mBaseApkPath = baseApkPath; this.mPath = path; + this.mCallback = callback; if (manifestArray != null) { versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0); @@ -2750,9 +2738,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, } private void assignDerivedFields2() { - mBaseAppInfoFlags = PackageInfoUtils.appInfoFlags(this, null); - mBaseAppInfoPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null); - mBaseAppInfoPrivateFlagsExt = PackageInfoUtils.appInfoPrivateFlagsExt(this, null); + mBaseAppInfoFlags = AppInfoUtils.appInfoFlags(this); + mBaseAppInfoPrivateFlags = AppInfoUtils.appInfoPrivateFlags(this); + mBaseAppInfoPrivateFlagsExt = AppInfoUtils.appInfoPrivateFlagsExt(this, + mCallback == null ? false : + mCallback.getHiddenApiWhitelistedApps().contains(this.packageName)); String baseAppDataDir = Environment.getDataDirectoryPath(getVolumeUuid()) + File.separator; String systemUserSuffix = File.separator + UserHandle.USER_SYSTEM + File.separator; mBaseAppDataCredentialProtectedDirForSystemUser = TextUtils.safeIntern( @@ -3087,7 +3077,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, appInfo.primaryCpuAbi = primaryCpuAbi; appInfo.secondaryCpuAbi = secondaryCpuAbi; appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir; - appInfo.seInfoUser = SELinuxUtil.COMPLETE_STR; + appInfo.seInfoUser = SEInfoUtil.COMPLETE_STR; appInfo.uid = uid; return appInfo; } diff --git a/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java b/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java index d9625050d28c..a670c6d5aff4 100644 --- a/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java +++ b/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.permission; +package com.android.internal.pm.permission; import android.Manifest; import android.annotation.NonNull; @@ -67,7 +67,7 @@ public class CompatibilityPermissionInfo { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -97,10 +97,10 @@ public class CompatibilityPermissionInfo { } @DataClass.Generated( - time = 1627674427184L, + time = 1701338392152L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final int mSdkVersion\npublic static final android.content.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genBuilder=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java", + inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final int mSdkVersion\npublic static final com.android.internal.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/core/java/com/android/internal/pm/pkg/SEInfoUtil.java b/core/java/com/android/internal/pm/pkg/SEInfoUtil.java new file mode 100644 index 000000000000..a6988829ca92 --- /dev/null +++ b/core/java/com/android/internal/pm/pkg/SEInfoUtil.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.pm.pkg; + +/** + * Utility methods that need to be used in application space. + * @hide + */ +public final class SEInfoUtil { + + /** Append to existing seinfo label for instant apps @hide */ + public static final String INSTANT_APP_STR = ":ephemeralapp"; + + /** Append to existing seinfo when modifications are complete @hide */ + public static final String COMPLETE_STR = ":complete"; +} diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java b/core/java/com/android/internal/pm/pkg/component/ComponentMutateUtils.java index 1964df0853fd..fd5f0f079da9 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ComponentMutateUtils.java @@ -14,19 +14,11 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.Nullable; -import com.android.internal.pm.pkg.component.ParsedActivity; -import com.android.internal.pm.pkg.component.ParsedComponent; -import com.android.internal.pm.pkg.component.ParsedMainComponent; -import com.android.internal.pm.pkg.component.ParsedPermission; -import com.android.internal.pm.pkg.component.ParsedPermissionGroup; -import com.android.internal.pm.pkg.component.ParsedProcess; -import com.android.internal.pm.pkg.component.ParsedProvider; - /** * Contains mutation methods so that code doesn't have to cast to the Impl. Meant to eventually * be removed once all post-parsing mutation is moved to parsing. diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java index 019ca1315af8..db08005c833e 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.AttrRes; import android.annotation.NonNull; @@ -29,14 +29,9 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.text.TextUtils; -import com.android.internal.pm.pkg.component.ParsedComponent; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; -import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.PackageUserState; -import com.android.server.pm.pkg.PackageUserStateUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -173,16 +168,4 @@ public class ComponentParseUtils { public static int getIcon(ParsedComponent component) { return component.getIcon(); } - - public static boolean isMatch(PackageUserState state, boolean isSystem, - boolean isPackageEnabled, ParsedMainComponent component, long flags) { - return PackageUserStateUtils.isMatch(state, isSystem, isPackageEnabled, - component.isEnabled(), component.isDirectBootAware(), component.getName(), flags); - } - - public static boolean isEnabled(PackageUserState state, boolean isPackageEnabled, - ParsedMainComponent parsedComponent, long flags) { - return PackageUserStateUtils.isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(), - parsedComponent.getName(), flags); - } } diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java index dd54cfca6518..0b045919fb13 100644 --- a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java +++ b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.content.pm.parsing.result.ParseInput; @@ -27,7 +27,6 @@ import android.util.ArraySet; import com.android.internal.R; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.SystemConfig; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -48,9 +47,8 @@ public class InstallConstraintsTagParser { * @hide */ public static ParseResult<ParsingPackage> parseInstallConstraints( - ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) - throws XmlPullParserException, IOException { - Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist(); + ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, + Set<String> allowlist) throws XmlPullParserException, IOException { if (!allowlist.contains(pkg.getPackageName())) { return input.skip("install-constraints cannot be used by this package"); } diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java index f02790189cc0..2f977eea127d 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; @@ -22,8 +22,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForStringSet; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForStringSet; import android.annotation.NonNull; import android.annotation.Nullable; @@ -36,10 +36,9 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.ArraySet; -import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import java.util.Collections; import java.util.Locale; @@ -380,7 +379,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -696,10 +695,10 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse } @DataClass.Generated( - time = 1669437519576L, + time = 1701338377709L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java", - inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java", + inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java index 64985bdfd54f..c3f7dab3c46a 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static com.android.server.pm.pkg.component.ComponentParseUtils.flag; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; -import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; +import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; import android.annotation.NonNull; import android.annotation.Nullable; @@ -48,11 +48,10 @@ import android.view.WindowManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.ArrayUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java index cfed19aa0934..27f7eee1a308 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedApexSystemService; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; @@ -60,7 +59,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -248,9 +247,9 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par }; @DataClass.Generated( - time = 1643723578605L, + time = 1701710844088L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java", + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java", inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceUtils.java index d3fb29b8aa66..c69213f11f85 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.R; import android.annotation.NonNull; @@ -25,8 +25,6 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.text.TextUtils; -import com.android.internal.pm.pkg.component.ParsedApexSystemService; - import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java index 62b994724346..e3bfb38802db 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.StringRes; @@ -22,7 +22,6 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedAttribution; import com.android.internal.util.DataClass; import java.util.ArrayList; @@ -60,7 +59,7 @@ public class ParsedAttributionImpl implements ParsedAttribution, Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -207,10 +206,10 @@ public class ParsedAttributionImpl implements ParsedAttribution, Parcelable { }; @DataClass.Generated( - time = 1641431950829L, + time = 1701338881658L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java", - inputSignatures = "static final int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedAttribution, android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java", + inputSignatures = "static final int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedAttribution, android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionUtils.java index 411220ae42e8..ee5c3204ccd1 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,7 +26,6 @@ import android.content.res.XmlResourceParser; import android.util.ArraySet; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedAttribution; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java index 512e5c7023c7..7ee22f30ace0 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import static java.util.Collections.emptyMap; @@ -32,12 +32,10 @@ import android.text.TextUtils; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedComponent; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; -import com.android.server.pm.pkg.parsing.ParsingUtils; import java.util.ArrayList; import java.util.Collections; @@ -200,7 +198,7 @@ public abstract class ParsedComponentImpl implements ParsedComponent, Parcelable // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -306,10 +304,10 @@ public abstract class ParsedComponentImpl implements ParsedComponent, Parcelable } @DataClass.Generated( - time = 1641414207885L, + time = 1701445673589L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java", - inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate int icon\nprivate int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate int logo\nprivate int banner\nprivate int descriptionRes\nprivate int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<android.content.pm.parsing.component.ParsedIntentInfoImpl> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\n void addIntent(android.content.pm.parsing.component.ParsedIntentInfoImpl)\n void addProperty(android.content.pm.PackageManager.Property)\npublic android.content.pm.parsing.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @android.annotation.NonNull @java.lang.Override android.os.Bundle getMetaData()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.parsing.component.ParsedIntentInfo> getIntents()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java", + inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate int icon\nprivate int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate int logo\nprivate int banner\nprivate int descriptionRes\nprivate int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<com.android.internal.pm.pkg.component.ParsedIntentInfoImpl> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\npublic void addIntent(com.android.internal.pm.pkg.component.ParsedIntentInfoImpl)\npublic void addProperty(android.content.pm.PackageManager.Property)\npublic com.android.internal.pm.pkg.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @android.annotation.NonNull @java.lang.Override android.os.Bundle getMetaData()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.internal.pm.pkg.component.ParsedIntentInfo> getIntents()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponentUtils.java index 9322cf0e90f6..9e2548b3bce3 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponentUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; import android.annotation.NonNull; import android.content.pm.PackageManager; @@ -32,8 +32,8 @@ import android.util.TypedValue; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; /** @hide */ class ParsedComponentUtils { diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java index 7bfad14d669a..07322e9dd912 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,7 +26,6 @@ import android.os.Parcelable; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -112,7 +111,7 @@ public class ParsedInstrumentationImpl extends ParsedComponentImpl implements // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -166,10 +165,10 @@ public class ParsedInstrumentationImpl extends ParsedComponentImpl implements } @DataClass.Generated( - time = 1641431951575L, + time = 1701445763455L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java", - inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate boolean handleProfiling\nprivate boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedInstrumentationImpl> CREATOR\npublic android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedInstrumentation, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java", + inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate boolean handleProfiling\nprivate boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedInstrumentationImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic com.android.internal.pm.pkg.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedInstrumentation, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationUtils.java index a7116949b911..661c8b421fb4 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; import android.annotation.NonNull; import android.content.pm.parsing.result.ParseInput; @@ -26,7 +26,6 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.pm.pkg.parsing.ParsingPackage; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java index ab9404310078..adb49e9fde6d 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,7 +23,6 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.util.DataClass; /** @@ -54,7 +53,7 @@ public class ParsedIntentInfoImpl implements ParsedIntentInfo, Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -170,10 +169,10 @@ public class ParsedIntentInfoImpl implements ParsedIntentInfo, Parcelable { }; @DataClass.Generated( - time = 1641431952314L, + time = 1701445800363L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java", - inputSignatures = "private boolean mHasDefault\nprivate int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedIntentInfo, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java", + inputSignatures = "private boolean mHasDefault\nprivate int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedIntentInfo, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java index e5e214d2292b..c6683cfc8331 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; import android.annotation.NonNull; import android.content.Intent; @@ -31,10 +31,9 @@ import android.util.Slog; import android.util.TypedValue; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java index f322eef8c3a3..bb8f565d2032 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -25,7 +25,6 @@ import android.os.Parcelable; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -133,7 +132,7 @@ public class ParsedMainComponentImpl extends ParsedComponentImpl implements Pars // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -227,10 +226,10 @@ public class ParsedMainComponentImpl extends ParsedComponentImpl implements Pars } @DataClass.Generated( - time = 1641414540422L, + time = 1701447884766L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java", - inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate boolean directBootAware\nprivate boolean enabled\nprivate boolean exported\nprivate int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedMainComponentImpl> CREATOR\npublic android.content.pm.parsing.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java", + inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate boolean directBootAware\nprivate boolean enabled\nprivate boolean exported\nprivate int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedMainComponentImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java index 8268f0fdfa3e..7e56180f72ce 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,10 +31,8 @@ import android.os.Build; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; -import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java index afe37bc3274c..3622019f36b9 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedPermissionGroup; import com.android.internal.util.DataClass; /** @@ -75,7 +74,7 @@ public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -172,10 +171,10 @@ public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements }; @DataClass.Generated( - time = 1642132854167L, + time = 1701445837884L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java", - inputSignatures = "private int requestDetailRes\nprivate int backgroundRequestRes\nprivate int backgroundRequestDetailRes\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\npublic @java.lang.Override @com.android.internal.util.DataClass.Generated.Member void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.server.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java", + inputSignatures = "private int requestDetailRes\nprivate int backgroundRequestRes\nprivate int backgroundRequestDetailRes\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java index 69e33c8f281e..4dcce131168b 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.Nullable; @@ -24,8 +24,6 @@ import android.text.TextUtils; import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedPermission; -import com.android.internal.pm.pkg.component.ParsedPermissionGroup; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -148,7 +146,7 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -162,7 +160,7 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP int requestRes, int protectionLevel, boolean tree, - @Nullable ParsedPermissionGroupImpl parsedPermissionGroup, + @Nullable ParsedPermissionGroup parsedPermissionGroup, @Nullable Set<String> knownCerts) { this.backgroundPermission = backgroundPermission; this.group = group; @@ -237,10 +235,10 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP } @DataClass.Generated( - time = 1641414649731L, + time = 1701445829812L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java", - inputSignatures = "private static com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate int requestRes\nprivate int protectionLevel\nprivate boolean tree\nprivate @android.annotation.Nullable android.content.pm.parsing.component.ParsedPermissionGroupImpl parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedPermissionImpl> CREATOR\npublic android.content.pm.parsing.component.ParsedPermissionGroup getParsedPermissionGroup()\npublic android.content.pm.parsing.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected void setKnownCert(java.lang.String)\nprotected void setKnownCerts(java.lang.String[])\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownCerts()\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java", + inputSignatures = "private static final com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate int requestRes\nprivate int protectionLevel\nprivate boolean tree\nprivate @android.annotation.Nullable com.android.internal.pm.pkg.component.ParsedPermissionGroup parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedPermissionImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedPermissionGroup getParsedPermissionGroup()\npublic com.android.internal.pm.pkg.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected void setKnownCert(java.lang.String)\nprotected void setKnownCerts(java.lang.String[])\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownCerts()\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionUtils.java index 4b45d3742a2a..5651c1ca247f 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; import android.annotation.NonNull; import android.content.pm.PermissionInfo; @@ -31,10 +31,8 @@ import android.util.EventLog; import android.util.Slog; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedPermission; -import com.android.internal.pm.pkg.component.ParsedPermissionGroup; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java index 40e3670b9261..212fb867e7df 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import static java.util.Collections.emptySet; @@ -25,7 +25,6 @@ import android.os.Parcelable; import android.util.ArrayMap; import android.util.ArraySet; -import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; @@ -98,7 +97,7 @@ public class ParsedProcessImpl implements ParsedProcess, Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -304,10 +303,10 @@ public class ParsedProcessImpl implements ParsedProcess, Parcelable { }; @DataClass.Generated( - time = 1641431953775L, + time = 1701445656489L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java", - inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java", + inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(com.android.internal.pm.pkg.component.ParsedProcess)\npublic void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java index a84954950f44..3b2056e7892e 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.content.pm.ApplicationInfo; @@ -27,11 +27,10 @@ import android.util.ArrayMap; import android.util.ArraySet; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.XmlUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java index 81a3c17e2bb4..987fd4158418 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -28,7 +28,6 @@ import android.os.PatternMatcher; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -302,7 +301,7 @@ public class ParsedProviderImpl extends ParsedMainComponentImpl implements Parse time = 1642560323360L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java", - inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate boolean grantUriPermissions\nprivate boolean forceUriPermissions\nprivate boolean multiProcess\nprivate int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)") + inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate boolean grantUriPermissions\nprivate boolean forceUriPermissions\nprivate boolean multiProcess\nprivate int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic com.android.internal.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java index 0b28a1214072..5d82d0469d56 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.component.ComponentParseUtils.flag; -import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER; +import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag; +import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,9 +34,8 @@ import android.os.PatternMatcher; import android.util.Slog; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java index ca8c45d1383c..f4662d803296 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,8 +26,6 @@ import android.os.Parcelable; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedMainComponent; -import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -107,7 +105,7 @@ public class ParsedServiceImpl extends ParsedMainComponentImpl implements Parsed // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -141,10 +139,10 @@ public class ParsedServiceImpl extends ParsedMainComponentImpl implements Parsed } @DataClass.Generated( - time = 1641431954479L, + time = 1701445638370L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java", - inputSignatures = "private int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedServiceImpl> CREATOR\npublic android.content.pm.parsing.component.ParsedMainComponent setPermission(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java", + inputSignatures = "private int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedServiceImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedMainComponent setPermission(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java index 171ef594f6fd..a1dd19a3bc90 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.component.ComponentParseUtils.flag; +import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,9 +32,8 @@ import android.content.res.XmlResourceParser; import android.os.Build; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java index 78377a836651..fd131dfd00c7 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedUsesPermission; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; @@ -51,7 +50,7 @@ public class ParsedUsesPermissionImpl implements ParsedUsesPermission, Parcelabl // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -158,10 +157,10 @@ public class ParsedUsesPermissionImpl implements ParsedUsesPermission, Parcelabl }; @DataClass.Generated( - time = 1641431955242L, + time = 1701445626268L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java", - inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedUsesPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java", + inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.pm.pkg.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedUsesPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java index aa0fb2734382..dbe4fba5dfdb 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.parsing; +package com.android.internal.pm.pkg.parsing; import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; @@ -30,7 +30,7 @@ import static android.os.Build.VERSION_CODES.DONUT; import static android.os.Build.VERSION_CODES.O; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; import android.annotation.AnyRes; import android.annotation.CheckResult; @@ -57,7 +57,6 @@ import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseInput.DeferredError; import android.content.pm.parsing.result.ParseResult; -import android.content.pm.parsing.result.ParseTypeImpl; import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; @@ -90,43 +89,40 @@ import android.util.apk.ApkSignatureVerifier; import com.android.internal.R; import com.android.internal.os.ClassLoaderFactory; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.permission.CompatibilityPermissionInfo; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; +import com.android.internal.pm.pkg.component.ComponentParseUtils; +import com.android.internal.pm.pkg.component.InstallConstraintsTagParser; import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedActivityImpl; +import com.android.internal.pm.pkg.component.ParsedActivityUtils; import com.android.internal.pm.pkg.component.ParsedApexSystemService; +import com.android.internal.pm.pkg.component.ParsedApexSystemServiceUtils; import com.android.internal.pm.pkg.component.ParsedAttribution; +import com.android.internal.pm.pkg.component.ParsedAttributionUtils; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedInstrumentation; +import com.android.internal.pm.pkg.component.ParsedInstrumentationUtils; import com.android.internal.pm.pkg.component.ParsedIntentInfo; +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl; +import com.android.internal.pm.pkg.component.ParsedIntentInfoUtils; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.component.ParsedPermissionUtils; import com.android.internal.pm.pkg.component.ParsedProcess; +import com.android.internal.pm.pkg.component.ParsedProcessUtils; import com.android.internal.pm.pkg.component.ParsedProvider; +import com.android.internal.pm.pkg.component.ParsedProviderUtils; import com.android.internal.pm.pkg.component.ParsedService; +import com.android.internal.pm.pkg.component.ParsedServiceUtils; import com.android.internal.pm.pkg.component.ParsedUsesPermission; -import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; +import com.android.internal.pm.split.DefaultSplitAssetLoader; +import com.android.internal.pm.split.SplitAssetDependencyLoader; +import com.android.internal.pm.split.SplitAssetLoader; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; -import com.android.server.pm.SharedUidMigration; -import com.android.server.pm.parsing.pkg.PackageImpl; -import com.android.server.pm.permission.CompatibilityPermissionInfo; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ComponentParseUtils; -import com.android.server.pm.pkg.component.InstallConstraintsTagParser; -import com.android.server.pm.pkg.component.ParsedActivityImpl; -import com.android.server.pm.pkg.component.ParsedActivityUtils; -import com.android.server.pm.pkg.component.ParsedApexSystemServiceUtils; -import com.android.server.pm.pkg.component.ParsedAttributionUtils; -import com.android.server.pm.pkg.component.ParsedInstrumentationUtils; -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl; -import com.android.server.pm.pkg.component.ParsedIntentInfoUtils; -import com.android.server.pm.pkg.component.ParsedPermissionUtils; -import com.android.server.pm.pkg.component.ParsedProcessUtils; -import com.android.server.pm.pkg.component.ParsedProviderUtils; -import com.android.server.pm.pkg.component.ParsedServiceUtils; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; -import com.android.server.pm.split.DefaultSplitAssetLoader; -import com.android.server.pm.split.SplitAssetDependencyLoader; -import com.android.server.pm.split.SplitAssetLoader; import libcore.io.IoUtils; import libcore.util.EmptyArray; @@ -267,18 +263,6 @@ public class ParsingPackageUtils { public @interface ParseFlags {} /** - * @see #parseDefault(ParseInput, File, int, List, boolean) - */ - @NonNull - public static ParseResult<ParsedPackage> parseDefaultOneTime(File file, - @ParseFlags int parseFlags, - @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, - boolean collectCertificates) { - ParseInput input = ParseTypeImpl.forDefaultParsing().reset(); - return parseDefault(input, file, parseFlags, splitPermissions, collectCertificates); - } - - /** * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off * request, without caching the input object and without querying the internal system state for * feature support. @@ -287,30 +271,10 @@ public class ParsingPackageUtils { public static ParseResult<ParsedPackage> parseDefault(ParseInput input, File file, @ParseFlags int parseFlags, @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, - boolean collectCertificates) { - ParseResult<ParsedPackage> result; + boolean collectCertificates, Callback callback) { ParsingPackageUtils parser = new ParsingPackageUtils(null /*separateProcesses*/, - null /*displayMetrics*/, splitPermissions, new Callback() { - @Override - public boolean hasFeature(String feature) { - // Assume the device doesn't support anything. This will affect permission - // parsing and will force <uses-permission/> declarations to include all - // requiredNotFeature permissions and exclude all requiredFeature - // permissions. This mirrors the old behavior. - return false; - } - - @Override - public ParsingPackage startParsingPackage( - @NonNull String packageName, - @NonNull String baseApkPath, - @NonNull String path, - @NonNull TypedArray manifestArray, boolean isCoreApp) { - return PackageImpl.forParsing(packageName, baseApkPath, path, manifestArray, - isCoreApp); - } - }); + null /*displayMetrics*/, splitPermissions, callback); var parseResult = parser.parsePackage(input, file, parseFlags); if (parseResult.isError()) { return input.error(parseResult); @@ -1146,7 +1110,8 @@ public class ParsingPackageUtils { case TAG_RESTRICT_UPDATE: return parseRestrictUpdateHash(flags, input, pkg, res, parser); case TAG_INSTALL_CONSTRAINTS: - return parseInstallConstraints(input, pkg, res, parser); + return parseInstallConstraints(input, pkg, res, parser, + mCallback.getInstallConstraintsAllowlist()); case TAG_QUERIES: return parseQueries(input, pkg, res, parser); default: @@ -1172,7 +1137,7 @@ public class ParsingPackageUtils { } boolean leaving = false; - if (!SharedUidMigration.isDisabled()) { + if (PackageManager.ENABLE_SHARED_UID_MIGRATION) { int max = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa); leaving = (max != 0) && (max < Build.VERSION.RESOURCES_SDK_INT); } @@ -1858,10 +1823,11 @@ public class ParsingPackageUtils { return input.success(pkg); } - private static ParseResult<ParsingPackage> parseInstallConstraints( - ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) + private static ParseResult<ParsingPackage> parseInstallConstraints(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser, Set<String> allowlist) throws IOException, XmlPullParserException { - return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser); + return InstallConstraintsTagParser.parseInstallConstraints( + input, pkg, res, parser, allowlist); } private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg, @@ -3485,5 +3451,9 @@ public class ParsingPackageUtils { ParsingPackage startParsingPackage(@NonNull String packageName, @NonNull String baseApkPath, @NonNull String path, @NonNull TypedArray manifestArray, boolean isCoreApp); + + @NonNull Set<String> getHiddenApiWhitelistedApps(); + + @NonNull Set<String> getInstallConstraintsAllowlist(); } } diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingUtils.java index 1d159554e8a7..26822c649db7 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingUtils.java @@ -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. @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.parsing; +package com.android.internal.pm.pkg.parsing; -import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER; +import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,10 +31,9 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.pm.pkg.component.ParsedIntentInfo; -import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl; import com.android.internal.util.Parcelling; import com.android.internal.util.XmlUtils; -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java b/core/java/com/android/internal/pm/split/DefaultSplitAssetLoader.java index 0bb969f488fe..50c62437b192 100644 --- a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java +++ b/core/java/com/android/internal/pm/split/DefaultSplitAssetLoader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.pm.split; +package com.android.internal.pm.split; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; @@ -21,9 +21,9 @@ import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; import com.android.internal.util.ArrayUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; import libcore.io.IoUtils; diff --git a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java b/core/java/com/android/internal/pm/split/SplitAssetDependencyLoader.java index 56d92fbc95a2..c166cdcf9451 100644 --- a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java +++ b/core/java/com/android/internal/pm/split/SplitAssetDependencyLoader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.pm.split; +package com.android.internal.pm.split; import android.annotation.NonNull; import android.content.pm.parsing.ApkLiteParseUtils; @@ -24,8 +24,8 @@ import android.content.res.AssetManager; import android.os.Build; import android.util.SparseArray; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; import libcore.io.IoUtils; diff --git a/services/core/java/com/android/server/pm/split/SplitAssetLoader.java b/core/java/com/android/internal/pm/split/SplitAssetLoader.java index 845015916e60..c7c409d826ba 100644 --- a/services/core/java/com/android/server/pm/split/SplitAssetLoader.java +++ b/core/java/com/android/internal/pm/split/SplitAssetLoader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.pm.split; +package com.android.internal.pm.split; import android.content.res.ApkAssets; import android.content.res.AssetManager; 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/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 28fd2b488426..bf8e6135fd01 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -897,13 +897,26 @@ public class LockPatternUtils { } /** - * Returns true if {@code userHandle} is a managed profile with separate challenge. + * Returns true if {@code userHandle} is a profile with separate challenge. + * <p> + * Returns false if {@code userHandle} is a profile with unified challenge, a profile whose + * credential is not shareable with its parent, or a non-profile user. */ public boolean isSeparateProfileChallengeEnabled(int userHandle) { return isCredentialSharableWithParent(userHandle) && hasSeparateChallenge(userHandle); } /** + * Returns true if {@code userHandle} is a profile with unified challenge. + * <p> + * Returns false if {@code userHandle} is a profile with separate challenge, a profile whose + * credential is not shareable with its parent, or a non-profile user. + */ + public boolean isProfileWithUnifiedChallenge(int userHandle) { + return isCredentialSharableWithParent(userHandle) && !hasSeparateChallenge(userHandle); + } + + /** * Returns true if {@code userHandle} is a managed profile with unified challenge. */ public boolean isManagedProfileWithUnifiedChallenge(int userHandle) { diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java index c88763ce6c97..18d5f6db6ac9 100644 --- a/core/java/com/android/internal/widget/LockscreenCredential.java +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -134,12 +134,12 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } /** - * Creates a LockscreenCredential object representing a managed password for profile with - * unified challenge. This credentiall will have type {@code CREDENTIAL_TYPE_PASSWORD} for now. - * TODO: consider add a new credential type for this. This can then supersede the - * isLockTiedToParent argument in various places in LSS. + * Creates a LockscreenCredential object representing the system-generated, system-managed + * password for a profile with unified challenge. This credential has type {@code + * CREDENTIAL_TYPE_PASSWORD} for now. TODO: consider add a new credential type for this. This + * can then supersede the isLockTiedToParent argument in various places in LSS. */ - public static LockscreenCredential createManagedPassword(@NonNull byte[] password) { + public static LockscreenCredential createUnifiedProfilePassword(@NonNull byte[] password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, Arrays.copyOf(password, password.length), /* hasInvalidChars= */ false); } diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java index f86595f1ea7b..adb0c6959f12 100644 --- a/core/java/com/android/server/pm/pkg/AndroidPackage.java +++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java @@ -58,6 +58,7 @@ import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.pm.pkg.component.ParsedUsesPermission; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import java.security.PublicKey; import java.util.List; @@ -690,7 +691,7 @@ public interface AndroidPackage { /** * The names of packages to adopt ownership of permissions from, parsed under {@link - * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}. + * ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}. * * @see R.styleable#AndroidManifestOriginalPackage_name * @hide @@ -795,7 +796,7 @@ public interface AndroidPackage { /** * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link - * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}. + * ParsingPackageUtils#TAG_KEY_SETS}. * * @see R.styleable#AndroidManifestKeySet * @see R.styleable#AndroidManifestPublicKey @@ -1266,7 +1267,7 @@ public interface AndroidPackage { /** * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link - * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}. + * ParsingPackageUtils#TAG_KEY_SETS}. * * @see R.styleable#AndroidManifestUpgradeKeySet * @hide 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/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp index 87ab4969040e..54c4cd50a902 100644 --- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp +++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp @@ -63,6 +63,7 @@ class NativeCommandBuffer { std::optional<std::pair<char*, char*>> readLine(FailFn fail_fn) { char* result = mBuffer + mNext; while (true) { + // We have scanned up to, but not including mNext for this line's newline. if (mNext == mEnd) { if (mEnd == MAX_COMMAND_BYTES) { return {}; @@ -89,7 +90,7 @@ class NativeCommandBuffer { } else { mNext = nl - mBuffer + 1; if (--mLinesLeft < 0) { - fail_fn("ZygoteCommandBuffer.readLine attempted to read past mEnd of command"); + fail_fn("ZygoteCommandBuffer.readLine attempted to read past end of command"); } return std::make_pair(result, nl); } @@ -125,8 +126,8 @@ class NativeCommandBuffer { mEnd += lineLen + 1; } - // Clear mBuffer, start reading new command, return the number of arguments, leaving mBuffer - // positioned at the beginning of first argument. Return 0 on EOF. + // Start reading new command, return the number of arguments, leaving mBuffer positioned at the + // beginning of first argument. Return 0 on EOF. template<class FailFn> int getCount(FailFn fail_fn) { mLinesLeft = 1; @@ -451,11 +452,14 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( (CREATE_ERROR("Write unexpectedly returned short: %d < 5", res)); } } - // Clear buffer and get count from next command. - n_buffer->clear(); for (;;) { + // Clear buffer and get count from next command. + n_buffer->clear(); // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect. int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); + if (poll_res < 0) { + fail_fn_z(CREATE_ERROR("Poll failed: %d: %s", errno, strerror(errno))); + } if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) { if (n_buffer->getCount(fail_fn_z) != 0) { break; 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/strings.xml b/core/res/res/values/strings.xml index 1f6ac80a133c..4596ca74bf8f 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5252,6 +5252,8 @@ <!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] --> <string name="zen_mode_default_every_night_name">Sleeping</string> + <!-- Zen mode - Trigger description of the rule, indicating which app owns it. [CHAR_LIMIT=100] --> + <string name="zen_mode_implicit_trigger_description">Managed by <xliff:g id="app_name">%1$s</xliff:g></string> <!-- Zen mode - Condition summary when a rule is activated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] --> <string name="zen_mode_implicit_activated">On</string> <!-- Zen mode - Condition summary when a rule is deactivated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5791ddb8addf..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" /> @@ -2586,6 +2587,7 @@ <java-symbol type="string" name="zen_mode_default_weekends_name" /> <java-symbol type="string" name="zen_mode_default_events_name" /> <java-symbol type="string" name="zen_mode_default_every_night_name" /> + <java-symbol type="string" name="zen_mode_implicit_trigger_description" /> <java-symbol type="string" name="zen_mode_implicit_activated" /> <java-symbol type="string" name="zen_mode_implicit_deactivated" /> <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" /> diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java index ba2ea88e8e01..1925588e8904 100644 --- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java +++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java @@ -19,15 +19,20 @@ package android.app; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; +import static org.junit.Assert.assertThrows; + import android.content.ComponentName; import android.net.Uri; import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.google.common.base.Strings; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,6 +43,9 @@ import java.lang.reflect.Field; public class AutomaticZenRuleTest { private static final String CLASS = "android.app.AutomaticZenRule"; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Test public void testLongFields_inConstructor() { String longString = Strings.repeat("A", 65536); @@ -100,6 +108,7 @@ public class AutomaticZenRuleTest { } @Test + @EnableFlags(Flags.FLAG_MODES_API) public void testLongInputsFromParcel() { // Create a rule with long fields, set directly via reflection so that we can confirm that // a rule with too-long fields that comes in via a parcel has its fields truncated directly. @@ -152,6 +161,69 @@ public class AutomaticZenRuleTest { fromParcel.getOwner().getPackageName().length()); assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, fromParcel.getOwner().getClassName().length()); - assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, rule.getTriggerDescription().length()); + assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, fromParcel.getTriggerDescription().length()); + } + + @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) + .build(); + rule.validate(); // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_builderWithoutType_succeeds() throws Exception { + AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build(); + rule.validate(); // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_constructorWithoutType_succeeds() throws Exception { + AutomaticZenRule rule = new AutomaticZenRule("rule", new ComponentName("pkg", "cps"), + new ComponentName("pkg", "activity"), Uri.parse("condition"), null, + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + rule.validate(); // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_invalidType_throws() throws Exception { + AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build(); + + // Set the field via reflection. + Field typeField = AutomaticZenRule.class.getDeclaredField("mType"); + typeField.setAccessible(true); + typeField.set(rule, 100); + + assertThrows(IllegalArgumentException.class, rule::validate); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void setType_invalidType_throws() { + AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build(); + + assertThrows(IllegalArgumentException.class, () -> rule.setType(100)); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void setTypeBuilder_invalidType_throws() { + AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("rule", Uri.parse("uri")); + + assertThrows(IllegalArgumentException.class, () -> builder.setType(100)); } } diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java index 8c231de5598f..e7b5dff60110 100644 --- a/core/tests/coretests/src/android/os/BundleTest.java +++ b/core/tests/coretests/src/android/os/BundleTest.java @@ -197,7 +197,6 @@ public class BundleTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void kindofEquals_bothParcelled_same() { Bundle bundle1 = new Bundle(); bundle1.putString("StringKey", "S"); @@ -215,7 +214,6 @@ public class BundleTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void kindofEquals_bothParcelled_different() { Bundle bundle1 = new Bundle(); bundle1.putString("StringKey", "S"); @@ -247,7 +245,6 @@ public class BundleTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void kindofEquals_lazyValues() { Parcelable p1 = new CustomParcelable(13, "Tiramisu"); Parcelable p2 = new CustomParcelable(13, "Tiramisu"); @@ -281,7 +278,6 @@ public class BundleTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void kindofEquals_lazyValuesWithIdenticalParcels_returnsTrue() { Parcelable p1 = new CustomParcelable(13, "Tiramisu"); Parcelable p2 = new CustomParcelable(13, "Tiramisu"); diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java index 8cd6773936ef..851e61259241 100644 --- a/core/tests/coretests/src/android/os/MessageQueueTest.java +++ b/core/tests/coretests/src/android/os/MessageQueueTest.java @@ -16,6 +16,7 @@ package android.os; +import android.platform.test.annotations.IgnoreUnderRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.MediumTest; @@ -153,6 +154,7 @@ public class MessageQueueTest { @Test @MediumTest + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testFieldIntegrity() throws Exception { TestHandlerThread tester = new TestFieldIntegrityHandler() { diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java index 5bbd2219e2f0..26f6d696768a 100644 --- a/core/tests/coretests/src/android/os/ParcelTest.java +++ b/core/tests/coretests/src/android/os/ParcelTest.java @@ -132,7 +132,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenSameData() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -169,7 +168,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenDifferentData() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -186,7 +184,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenLimitOutOfBounds_throws() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -213,7 +210,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenLengthZero() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -232,7 +228,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenNegativeLength_throws() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -248,7 +243,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenNegativeOffset_throws() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java index 612562eb22dc..e94273e1ada7 100644 --- a/core/tests/coretests/src/android/service/notification/ConditionTest.java +++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java @@ -21,9 +21,12 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; +import static org.junit.Assert.assertThrows; + import android.app.Flags; import android.net.Uri; import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -36,6 +39,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; @RunWith(AndroidJUnit4.class) @SmallTest @@ -194,4 +198,59 @@ public class ConditionTest { Condition fromParcel = new Condition(parcel); assertThat(fromParcel).isEqualTo(cond); } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void constructor_unspecifiedSource_succeeds() { + new Condition(Uri.parse("id"), "Summary", Condition.STATE_TRUE); + // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void constructor_validSource_succeeds() { + new Condition(Uri.parse("id"), "Summary", Condition.STATE_TRUE, Condition.SOURCE_CONTEXT); + // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void constructor_invalidSource_throws() { + assertThrows(IllegalArgumentException.class, + () -> new Condition(Uri.parse("uri"), "Summary", Condition.STATE_TRUE, 1000)); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void constructor_parcelWithInvalidSource_throws() { + Condition original = new Condition(Uri.parse("condition"), "Summary", Condition.STATE_TRUE, + Condition.SOURCE_SCHEDULE); + Parcel parcel = Parcel.obtain(); + original.writeToParcel(parcel, 0); + + // Tweak the parcel to contain and invalid source value. + parcel.setDataPosition(parcel.dataPosition() - 8); // going back two int fields. + parcel.writeInt(100); + parcel.setDataPosition(0); + + assertThrows(IllegalArgumentException.class, () -> new Condition(parcel)); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_invalidSource_throws() throws Exception { + Condition condition = new Condition(Uri.parse("condition"), "Summary", Condition.STATE_TRUE, + Condition.SOURCE_SCHEDULE); + + Field typeField = Condition.class.getDeclaredField("source"); + + // Reflection on reflection (ugh) to make a final field non-final + Field fieldAccessFlagsField = Field.class.getDeclaredField("accessFlags"); + fieldAccessFlagsField.setAccessible(true); + fieldAccessFlagsField.setInt(typeField, typeField.getModifiers() & ~Modifier.FINAL); + + typeField.setInt(condition, 30); + + assertThrows(IllegalArgumentException.class, condition::validate); + } } diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java index 1df1090e0343..1c72185ea93c 100644 --- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java +++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java @@ -37,6 +37,7 @@ public class SparseSetArrayTest { public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Test + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testAddAll() { final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>(); diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java index e8246c83e086..ac659e1bc593 100644 --- a/core/tests/utiltests/src/android/util/TimeUtilsTest.java +++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java @@ -18,8 +18,12 @@ package android.util; import static org.junit.Assert.assertEquals; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + import androidx.test.runner.AndroidJUnit4; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,6 +33,9 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class TimeUtilsTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + public static final long SECOND_IN_MILLIS = 1000; public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60; public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; @@ -78,6 +85,7 @@ public class TimeUtilsTest { } @Test + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testDumpTime() { assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> { TimeUtils.dumpTime(pw, 1672556400000L); @@ -91,6 +99,7 @@ public class TimeUtilsTest { } @Test + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testFormatForLogging() { assertEquals("unknown", TimeUtils.formatForLogging(0)); assertEquals("unknown", TimeUtils.formatForLogging(-1)); @@ -99,6 +108,7 @@ public class TimeUtilsTest { } @Test + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testLogTimeOfDay() { assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L)); } 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/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java index 15d14e87fcf6..b315f94b5d00 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -241,7 +241,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, for (int i = 0; i < displays.length; i++) { DisplayAddress.Physical address = (DisplayAddress.Physical) displays[i].getAddress(); - if (mRearDisplayAddress == address.getPhysicalDisplayId()) { + if (address != null && mRearDisplayAddress == address.getPhysicalDisplayId()) { rearDisplayMetrics = new DisplayMetrics(); final Display rearDisplay = displays[i]; 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/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 0f0fbd9cc12f..f801b0d01084 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -83,6 +83,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { private int mStartPos; private GestureDetector mDoubleTapDetector; private boolean mInteractive; + private boolean mHideHandle; private boolean mSetTouchRegion = true; private int mLastDraggingPosition; private int mHandleRegionWidth; @@ -211,11 +212,8 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { } /** Sets up essential dependencies of the divider bar. */ - public void setup( - SplitLayout layout, - SplitWindowManager splitWindowManager, - SurfaceControlViewHost viewHost, - InsetsState insetsState) { + public void setup(SplitLayout layout, SplitWindowManager splitWindowManager, + SurfaceControlViewHost viewHost, InsetsState insetsState) { mSplitLayout = layout; mSplitWindowManager = splitWindowManager; mViewHost = viewHost; @@ -277,6 +275,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { R.dimen.docked_stack_divider_lift_elevation); mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener()); mInteractive = true; + mHideHandle = false; setOnTouchListener(this); mHandle.setAccessibilityDelegate(mHandleDelegate); setWillNotDraw(false); @@ -469,10 +468,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { void setInteractive(boolean interactive, boolean hideHandle, String from) { if (interactive == mInteractive) return; ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, - "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive", - from); + "Set divider bar %s hide handle=%b from %s", + interactive ? "interactive" : "non-interactive", hideHandle, from); mInteractive = interactive; - if (!mInteractive && hideHandle && mMoving) { + mHideHandle = hideHandle; + if (!mInteractive && mHideHandle && mMoving) { final int position = mSplitLayout.getDividePosition(); mSplitLayout.flingDividePosition( mLastDraggingPosition, @@ -482,7 +482,15 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mMoving = false; } releaseTouching(); - mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE); + mHandle.setVisibility(!mInteractive && mHideHandle ? View.INVISIBLE : View.VISIBLE); + } + + boolean isInteractive() { + return mInteractive; + } + + boolean isHandleHidden() { + return mHideHandle; } private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index b699533374df..53caddb52f23 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -59,6 +59,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; @@ -70,6 +71,7 @@ import com.android.wm.shell.common.InteractionJankMonitorUtils; import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.io.PrintWriter; import java.util.function.Consumer; @@ -420,7 +422,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void init() { if (mInitialized) return; mInitialized = true; - mSplitWindowManager.init(this, mInsetsState); + mSplitWindowManager.init(this, mInsetsState, false /* isRestoring */); mDisplayImeController.addPositionProcessor(mImePositionProcessor); } @@ -442,14 +444,19 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** Releases and re-inflates {@link DividerView} on the root surface. */ - public void update(SurfaceControl.Transaction t) { + public void update(SurfaceControl.Transaction t, boolean resetImePosition) { if (!mInitialized) { init(); return; } mSplitWindowManager.release(t); - mImePositionProcessor.reset(); - mSplitWindowManager.init(this, mInsetsState); + if (resetImePosition) { + mImePositionProcessor.reset(); + } + mSplitWindowManager.init(this, mInsetsState, true /* isRestoring */); + // Update the surface positions again after recreating the divider in case nothing else + // triggers it + mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); } @Override @@ -868,6 +875,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange pw.println(prefix + TAG + ":"); pw.println(innerPrefix + "mAllowLeftRightSplitInPortrait=" + mAllowLeftRightSplitInPortrait); pw.println(innerPrefix + "mIsLeftRightSplit=" + mIsLeftRightSplit); + pw.println(innerPrefix + "mFreezeDividerWindow=" + mFreezeDividerWindow); + pw.println(innerPrefix + "mDimNonImeSide=" + mDimNonImeSide); + pw.println(innerPrefix + "mDividerPosition=" + mDividerPosition); pw.println(innerPrefix + "bounds1=" + mBounds1.toShortString()); pw.println(innerPrefix + "dividerBounds=" + mDividerBounds.toShortString()); pw.println(innerPrefix + "bounds2=" + mBounds2.toShortString()); @@ -1151,14 +1161,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mTargetYOffset = needOffset ? getTargetYOffset() : 0; if (mTargetYOffset != mLastYOffset) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Split IME animation starting, fromY=%d toY=%d", + mLastYOffset, mTargetYOffset); // Freeze the configuration size with offset to prevent app get a configuration // changed or relaunch. This is required to make sure client apps will calculate // insets properly after layout shifted. if (mTargetYOffset == 0) { mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this); } else { - mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset - mLastYOffset, - SplitLayout.this); + mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset, SplitLayout.this); } } @@ -1183,6 +1195,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void onImeEndPositioning(int displayId, boolean cancel, SurfaceControl.Transaction t) { if (displayId != mDisplayId || !mHasImeFocus || cancel) return; + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Split IME animation ending, canceled=%b", cancel); onProgress(1.0f); mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 00361d9dd9cf..8fb9bda539a0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -62,6 +62,10 @@ public final class SplitWindowManager extends WindowlessWindowManager { // Used to "pass" a transaction to WWM.remove so that view removal can be synchronized. private SurfaceControl.Transaction mSyncTransaction = null; + // For saving/restoring state + private boolean mLastDividerInteractive = true; + private boolean mLastDividerHandleHidden; + public interface ParentContainerCallbacks { void attachToParentSurface(SurfaceControl.Builder b); void onLeashReady(SurfaceControl leash); @@ -107,7 +111,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { } /** Inflates {@link DividerView} on to the root surface. */ - void init(SplitLayout splitLayout, InsetsState insetsState) { + void init(SplitLayout splitLayout, InsetsState insetsState, boolean isRestoring) { if (mDividerView != null || mViewHost != null) { throw new UnsupportedOperationException( "Try to inflate divider view again without release first"); @@ -130,6 +134,10 @@ public final class SplitWindowManager extends WindowlessWindowManager { lp.accessibilityTitle = mContext.getResources().getString(R.string.accessibility_divider); mViewHost.setView(mDividerView, lp); mDividerView.setup(splitLayout, this, mViewHost, insetsState); + if (isRestoring) { + mDividerView.setInteractive(mLastDividerInteractive, mLastDividerHandleHidden, + "restore_setup"); + } } /** @@ -138,6 +146,8 @@ public final class SplitWindowManager extends WindowlessWindowManager { */ void release(@Nullable SurfaceControl.Transaction t) { if (mDividerView != null) { + mLastDividerInteractive = mDividerView.isInteractive(); + mLastDividerHandleHidden = mDividerView.isHandleHidden(); mDividerView = null; } 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 449bef5608c9..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); @@ -1666,7 +1686,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { - mSplitLayout.update(finishT); + mSplitLayout.update(finishT, true /* resetImePosition */); mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash, getMainStageBounds()); mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash, @@ -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()) { @@ -1860,9 +1881,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration) && mMainStage.isActive()) { // Clear the divider remote animating flag as the divider will be re-rendered to apply - // the new rotation config. + // the new rotation config. Don't reset the IME state since those updates are not in + // sync with task info changes. mIsDividerRemoteAnimating = false; - mSplitLayout.update(null /* t */); + mSplitLayout.update(null /* t */, false /* resetImePosition */); onLayoutSizeChanged(mSplitLayout); } } @@ -1928,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(); @@ -2122,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(); @@ -2325,7 +2349,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ public void updateSurfaces(SurfaceControl.Transaction transaction) { updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false); - mSplitLayout.update(transaction); + mSplitLayout.update(transaction, true /* resetImePosition */); } private void onDisplayChange(int displayId, int fromRotation, int toRotation, @@ -2598,7 +2622,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final TransitionInfo.Change change = info.getChanges().get(iC); if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) { - mSplitLayout.update(startTransaction); + // Don't reset the IME state since those updates are not in sync with the + // display change transition + mSplitLayout.update(startTransaction, false /* resetImePosition */); } if (mMixedHandler.isEnteringPip(change, transitType)) { @@ -2699,7 +2725,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, startTransaction, finishTransaction, finishCallback)) { if (mSplitTransitions.isPendingResize(transition)) { // Only need to update in resize because divider exist before transition. - mSplitLayout.update(startTransaction); + mSplitLayout.update(startTransaction, true /* resetImePosition */); startTransaction.apply(); } return true; @@ -3242,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/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java index 145c8f0ab8af..636c6326d213 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java @@ -69,7 +69,7 @@ public class DividerViewTest extends ShellTestCase { SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager", mContext, configuration, mCallbacks); - splitWindowManager.init(mSplitLayout, new InsetsState()); + splitWindowManager.init(mSplitLayout, new InsetsState(), false /* isRestoring */); mDividerView = spy((DividerView) splitWindowManager.getDividerView()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java index 2e5078d86a8b..150aa13f2d00 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java @@ -59,7 +59,7 @@ public class SplitWindowManagerTests extends ShellTestCase { @Test @UiThreadTest public void testInitRelease() { - mSplitWindowManager.init(mSplitLayout, new InsetsState()); + mSplitWindowManager.init(mSplitLayout, new InsetsState(), false /* isRestoring */); assertThat(mSplitWindowManager.getSurfaceControl()).isNotNull(); mSplitWindowManager.release(null /* t */); assertThat(mSplitWindowManager.getSurfaceControl()).isNull(); 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/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 8445032293dd..69718a6c4b3e 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -43,12 +43,15 @@ cc_test { }, shared_libs: [ "libandroid_runtime", + "libbase", + "libinput", "libinputservice", "libhwui", "libgui", "libutils", ], static_libs: [ + "libflagtest", "libgmock", "libgtest", ], diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index d9efd3c2fd83..adfa91e96ebb 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <com_android_input_flags.h> +#include <flag_macros.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <input/PointerController.h> @@ -28,6 +30,8 @@ namespace android { +namespace input_flags = com::android::input::flags; + enum TestCursorType { CURSOR_TYPE_DEFAULT = 0, CURSOR_TYPE_HOVER, @@ -261,7 +265,20 @@ TEST_F(PointerControllerTest, useStylusTypeForStylusHover) { mPointerController->reloadPointerResources(); } -TEST_F(PointerControllerTest, updatePointerIcon) { +TEST_F_WITH_FLAGS(PointerControllerTest, setPresentationBeforeDisplayViewportDoesNotLoadResources, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(input_flags, enable_pointer_choreographer))) { + // Setting the presentation mode before a display viewport is set will not load any resources. + mPointerController->setPresentation(PointerController::Presentation::POINTER); + ASSERT_TRUE(mPolicy->noResourcesAreLoaded()); + + // When the display is set, then the resources are loaded. + ensureDisplayViewportIsSet(); + ASSERT_TRUE(mPolicy->allResourcesAreLoaded()); +} + +TEST_F_WITH_FLAGS(PointerControllerTest, updatePointerIcon, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(input_flags, + enable_pointer_choreographer))) { ensureDisplayViewportIsSet(); mPointerController->setPresentation(PointerController::Presentation::POINTER); mPointerController->unfade(PointerController::Transition::IMMEDIATE); @@ -277,6 +294,24 @@ TEST_F(PointerControllerTest, updatePointerIcon) { mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type)); } +TEST_F_WITH_FLAGS(PointerControllerTest, updatePointerIconWithChoreographer, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(input_flags, enable_pointer_choreographer))) { + // When PointerChoreographer is enabled, the presentation mode is set before the viewport. + mPointerController->setPresentation(PointerController::Presentation::POINTER); + ensureDisplayViewportIsSet(); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); + + int32_t type = CURSOR_TYPE_ADDITIONAL; + std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type); + EXPECT_CALL(*mPointerSprite, setVisible(true)); + EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); + EXPECT_CALL(*mPointerSprite, + setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)), + Field(&SpriteIcon::hotSpotX, hotspot.first), + Field(&SpriteIcon::hotSpotY, hotspot.second)))); + mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type)); +} + TEST_F(PointerControllerTest, setCustomPointerIcon) { ensureDisplayViewportIsSet(); mPointerController->unfade(PointerController::Transition::IMMEDIATE); diff --git a/media/OWNERS b/media/OWNERS index 4a6648e91af4..994a7b810009 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -21,7 +21,6 @@ wonsik@google.com include platform/frameworks/av:/media/janitors/media_solutions_OWNERS # SEO -sungsoo@google.com # SEA/KIR/BVE jtinker@google.com diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS index bbe5e06bb282..058c5be6af6c 100644 --- a/media/java/android/media/OWNERS +++ b/media/java/android/media/OWNERS @@ -2,7 +2,6 @@ fgoldfain@google.com elaurent@google.com lajos@google.com -sungsoo@google.com jmtrivi@google.com # go/android-fwk-media-solutions for info on areas of ownership. 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/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml index e3b93ba34045..f4641b9930cb 100644 --- a/packages/PackageInstaller/res/values/strings.xml +++ b/packages/PackageInstaller/res/values/strings.xml @@ -202,11 +202,6 @@ <!-- Dialog attributes to indicate parse errors --> <string name="Parse_error_dlg_text">There was a problem parsing the package.</string> - <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=30] --> - <string name="wear_not_allowed_dlg_title">Android Wear</string> - <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=none] --> - <string name="wear_not_allowed_dlg_text">Install/Uninstall actions not supported on Wear.</string> - <!-- Message that the app to be installed is being staged [CHAR LIMIT=50] --> <string name="message_staging">Staging app…</string> diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java index e07e9425808e..2da8c8c69ff8 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java @@ -156,10 +156,9 @@ public class UninstallAlertDialogFragment extends DialogFragment implements if (customUserManager.isUserOfType(USER_TYPE_PROFILE_MANAGED) && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) { messageBuilder.append(isArchive - ? getString(R.string.archive_application_text_current_user_work_profile, - userName) : getString( - R.string.uninstall_application_text_current_user_work_profile, - userName)); + ? getString(R.string.archive_application_text_current_user_work_profile) + : getString( + R.string.uninstall_application_text_current_user_work_profile)); } else if (customUserManager.isUserOfType(USER_TYPE_PROFILE_CLONE) && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) { mIsClonedApp = true; @@ -168,11 +167,11 @@ public class UninstallAlertDialogFragment extends DialogFragment implements } else if (Flags.allowPrivateProfile() && customUserManager.isPrivateProfile() && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) { - messageBuilder.append(isArchive ? getString( - R.string.archive_application_text_current_user_private_profile, - userName) : getString( - R.string.uninstall_application_text_current_user_private_profile, - userName)); + messageBuilder.append( + isArchive ? getString( + R.string.archive_application_text_current_user_private_profile) + : getString( + R.string.uninstall_application_text_current_user_private_profile)); } else if (isArchive) { messageBuilder.append( getString(R.string.archive_application_text_user, userName)); 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/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt index d0d2dc0083a6..e099f1124bf1 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt @@ -20,13 +20,17 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn +private const val TAG = "BroadcastReceiverFlow" + /** * A [BroadcastReceiver] flow for the given [intentFilter]. */ @@ -39,4 +43,6 @@ fun Context.broadcastReceiverFlow(intentFilter: IntentFilter): Flow<Intent> = ca registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED) awaitClose { unregisterReceiver(broadcastReceiver) } +}.catch { e -> + Log.e(TAG, "Error while broadcastReceiverFlow", e) }.conflate().flowOn(Dispatchers.Default) diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt index dfaf3c66ff8d..eef5225aef42 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt @@ -31,8 +31,10 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doThrow import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class BroadcastReceiverFlowTest { @@ -74,6 +76,18 @@ class BroadcastReceiverFlowTest { assertThat(onReceiveIsCalled).isTrue() } + @Test + fun broadcastReceiverFlow_unregisterReceiverThrowException_noCrash() = runBlocking { + context.stub { + on { unregisterReceiver(any()) } doThrow IllegalArgumentException() + } + val flow = context.broadcastReceiverFlow(INTENT_FILTER) + + flow.firstWithTimeoutOrNull() + + assertThat(registeredBroadcastReceiver).isNotNull() + } + private companion object { val INTENT_FILTER = IntentFilter() } 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 69b61c74625e..49ac0f864ed7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -28,10 +28,11 @@ 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.bluetooth.BluetoothStatusCodes; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -42,26 +43,30 @@ import android.os.Looper; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; -import android.util.Pair; +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; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; /** - * LocalBluetoothLeBroadcast provides an interface between the Settings app - * and the functionality of the local {@link BluetoothLeBroadcast}. - * Use the {@link BluetoothLeBroadcast.Callback} to get the result callback. + * LocalBluetoothLeBroadcast provides an interface between the Settings app and the functionality of + * the local {@link BluetoothLeBroadcast}. Use the {@link BluetoothLeBroadcast.Callback} to get the + * result callback. */ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { private static final String TAG = "LocalBluetoothLeBroadcast"; @@ -74,11 +79,12 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { // Order of this profile in device profiles list private static final int ORDINAL = 1; private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; - private static final Uri[] SETTINGS_URIS = new Uri[]{ - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO), - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE), - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME), - }; + private static final Uri[] SETTINGS_URIS = + new Uri[] { + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO), + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE), + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME), + }; private BluetoothLeBroadcast mServiceBroadcast; private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant; @@ -95,62 +101,82 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { private Executor mExecutor; private ContentResolver mContentResolver; private ContentObserver mSettingsObserver; + // Cached broadcast callbacks being register before service is connected. + private Map<BluetoothLeBroadcast.Callback, Executor> mCachedBroadcastCallbackExecutorMap = + new ConcurrentHashMap<>(); - private final ServiceListener mServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service connected: " + profile); - } - if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && !mIsBroadcastProfileReady) { - mServiceBroadcast = (BluetoothLeBroadcast) proxy; - mIsBroadcastProfileReady = true; - registerServiceCallBack(mExecutor, mBroadcastCallback); - List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata(); - if (!metadata.isEmpty()) { - updateBroadcastInfoFromBroadcastMetadata(metadata.get(0)); + private final ServiceListener mServiceListener = + new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service connected: " + profile); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) + && !mIsBroadcastProfileReady) { + mServiceBroadcast = (BluetoothLeBroadcast) proxy; + mIsBroadcastProfileReady = true; + registerServiceCallBack(mExecutor, mBroadcastCallback); + List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata(); + if (!metadata.isEmpty()) { + updateBroadcastInfoFromBroadcastMetadata(metadata.get(0)); + } + registerContentObserver(); + if (DEBUG) { + Log.d( + TAG, + "onServiceConnected: register " + + "mCachedBroadcastCallbackExecutorMap = " + + mCachedBroadcastCallbackExecutorMap); + } + mCachedBroadcastCallbackExecutorMap.forEach( + (callback, executor) -> + registerServiceCallBack(executor, callback)); + } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) + && !mIsBroadcastAssistantProfileReady) { + mIsBroadcastAssistantProfileReady = true; + mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy; + registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback); + } } - registerContentObserver(); - } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) - && !mIsBroadcastAssistantProfileReady) { - mIsBroadcastAssistantProfileReady = true; - mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy; - registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback); - } - } - @Override - public void onServiceDisconnected(int profile) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service disconnected"); - } - if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && mIsBroadcastProfileReady) { - mIsBroadcastProfileReady = false; - unregisterServiceCallBack(mBroadcastCallback); - } - if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) - && mIsBroadcastAssistantProfileReady) { - mIsBroadcastAssistantProfileReady = false; - unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback); - } + @Override + public void onServiceDisconnected(int profile) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service disconnected: " + profile); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) + && mIsBroadcastProfileReady) { + mIsBroadcastProfileReady = false; + unregisterServiceCallBack(mBroadcastCallback); + mCachedBroadcastCallbackExecutorMap.clear(); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) + && mIsBroadcastAssistantProfileReady) { + mIsBroadcastAssistantProfileReady = false; + unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback); + } - if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) { - unregisterContentObserver(); - } - } - }; + if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) { + unregisterContentObserver(); + } + } + }; private final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastStarted(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastStarted(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastStarted(), reason = " + + reason + + ", broadcastId = " + broadcastId); } setLatestBroadcastId(broadcastId); - setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true); + setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true); } @Override @@ -161,8 +187,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } @Override - public void onBroadcastMetadataChanged(int broadcastId, - @NonNull BluetoothLeBroadcastMetadata metadata) { + public void onBroadcastMetadataChanged( + int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) { if (DEBUG) { Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId); } @@ -172,8 +198,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { @Override public void onBroadcastStopped(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastStopped(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastStopped(), reason = " + + reason + + ", broadcastId = " + broadcastId); } @@ -191,37 +220,42 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { @Override public void onBroadcastUpdated(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastUpdated(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastUpdated(), reason = " + + reason + + ", broadcastId = " + broadcastId); } setLatestBroadcastId(broadcastId); - setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true); + setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true); } @Override public void onBroadcastUpdateFailed(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastUpdateFailed(), reason = " + + reason + + ", broadcastId = " + broadcastId); } } @Override - public void onPlaybackStarted(int reason, int broadcastId) { - } + public void onPlaybackStarted(int reason, int broadcastId) {} @Override - public void onPlaybackStopped(int reason, int broadcastId) { - } + public void onPlaybackStopped(int reason, int broadcastId) {} }; private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @Override - public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceAdded( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} + @Override public void onSearchStarted(int reason) {} @@ -238,38 +272,65 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {} @Override - public void onSourceAddFailed(@NonNull BluetoothDevice sink, - @NonNull BluetoothLeBroadcastMetadata source, int reason) {} + public void onSourceAddFailed( + @NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastMetadata source, + int reason) {} @Override - public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceModified( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} @Override - public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceModifyFailed( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} @Override - public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId, - int reason) { + public void onSourceRemoved( + @NonNull BluetoothDevice sink, int sourceId, int reason) { if (DEBUG) { - Log.d(TAG, "onSourceRemoved(), sink = " + sink + ", reason = " - + reason + ", sourceId = " + sourceId); + Log.d( + TAG, + "onSourceRemoved(), sink = " + + sink + + ", reason = " + + reason + + ", sourceId = " + + sourceId); } } @Override - public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId, - int reason) { + public void onSourceRemoveFailed( + @NonNull BluetoothDevice sink, int sourceId, int reason) { if (DEBUG) { - Log.d(TAG, "onSourceRemoveFailed(), sink = " + sink + ", reason = " - + reason + ", sourceId = " + sourceId); + Log.d( + TAG, + "onSourceRemoveFailed(), sink = " + + sink + + ", reason = " + + reason + + ", sourceId = " + + sourceId); } } @Override - public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId, - @NonNull BluetoothLeBroadcastReceiveState state) {} + public void onReceiveStateChanged( + @NonNull BluetoothDevice sink, + int sourceId, + @NonNull BluetoothLeBroadcastReceiveState state) { + if (DEBUG) { + Log.d( + TAG, + "onReceiveStateChanged(), sink = " + + sink + + ", sourceId = " + + sourceId + + ", state = " + + state); + } + } }; private class BroadcastSettingsObserver extends ContentObserver { @@ -296,8 +357,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { BluetoothAdapter.getDefaultAdapter() .getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST); BluetoothAdapter.getDefaultAdapter() - .getProfileProxy(context, mServiceListener, - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + .getProfileProxy( + context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); } /** @@ -312,20 +373,91 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } String programInfo = getProgramInfo(); if (DEBUG) { - Log.d(TAG, - "startBroadcast: language = " + language + " ,programInfo = " + programInfo); + Log.d(TAG, "startBroadcast: language = " + language + " ,programInfo = " + programInfo); } buildContentMetadata(language, programInfo); - mServiceBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata, + mServiceBroadcast.startBroadcast( + mBluetoothLeAudioContentMetadata, (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; } public void setProgramInfo(String programInfo) { - setProgramInfo(programInfo, /*updateContentResolver=*/ true); + setProgramInfo(programInfo, /* updateContentResolver= */ true); } private void setProgramInfo(String programInfo, boolean updateContentResolver) { @@ -344,8 +476,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, programInfo); + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, + programInfo); } } @@ -354,7 +488,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } public void setBroadcastCode(byte[] broadcastCode) { - setBroadcastCode(broadcastCode, /*updateContentResolver=*/ true); + setBroadcastCode(broadcastCode, /* updateContentResolver= */ true); } private void setBroadcastCode(byte[] broadcastCode, boolean updateContentResolver) { @@ -372,7 +506,9 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE, + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE, new String(broadcastCode, StandardCharsets.UTF_8)); } } @@ -401,8 +537,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, mAppSourceName); + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, + mAppSourceName); } } @@ -427,10 +565,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (mBluetoothLeBroadcastMetadata == null) { final List<BluetoothLeBroadcastMetadata> metadataList = mServiceBroadcast.getAllBroadcastMetadata(); - mBluetoothLeBroadcastMetadata = metadataList.stream() - .filter(i -> i.getBroadcastId() == mBroadcastId) - .findFirst() - .orElse(null); + mBluetoothLeBroadcastMetadata = + metadataList.stream() + .filter(i -> i.getBroadcastId() == mBroadcastId) + .findFirst() + .orElse(null); } return mBluetoothLeBroadcastMetadata; } @@ -440,22 +579,27 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "updateBroadcastInfoFromContentProvider: mContentResolver is null"); return; } - String programInfo = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO); + String programInfo = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO); if (programInfo == null) { programInfo = getDefaultValueOfProgramInfo(); } - setProgramInfo(programInfo, /*updateContentResolver=*/ false); + setProgramInfo(programInfo, /* updateContentResolver= */ false); - String prefBroadcastCode = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE); - byte[] broadcastCode = (prefBroadcastCode == null) ? getDefaultValueOfBroadcastCode() - : prefBroadcastCode.getBytes(StandardCharsets.UTF_8); - setBroadcastCode(broadcastCode, /*updateContentResolver=*/ false); + String prefBroadcastCode = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE); + byte[] broadcastCode = + (prefBroadcastCode == null) + ? getDefaultValueOfBroadcastCode() + : prefBroadcastCode.getBytes(StandardCharsets.UTF_8); + setBroadcastCode(broadcastCode, /* updateContentResolver= */ false); - String appSourceName = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME); - setAppSourceName(appSourceName, /*updateContentResolver=*/ false); + String appSourceName = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME); + setAppSourceName(appSourceName, /* updateContentResolver= */ false); } private void updateBroadcastInfoFromBroadcastMetadata( @@ -474,12 +618,12 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } BluetoothLeAudioContentMetadata contentMetadata = subgroup.get(0).getContentMetadata(); setProgramInfo(contentMetadata.getProgramInfo()); - setAppSourceName(getAppSourceName(), /*updateContentResolver=*/ true); + setAppSourceName(getAppSourceName(), /* updateContentResolver= */ true); } /** - * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system - * calls the corresponding callback {@link BluetoothLeBroadcast.Callback}. + * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system calls + * the corresponding callback {@link BluetoothLeBroadcast.Callback}. */ public void stopLatestBroadcast() { stopBroadcast(mBroadcastId); @@ -511,7 +655,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } String programInfo = getProgramInfo(); if (DEBUG) { - Log.d(TAG, + Log.d( + TAG, "updateBroadcast: language = " + language + " ,programInfo = " + programInfo); } mNewAppSourceName = appSourceName; @@ -519,50 +664,79 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { mServiceBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata); } - public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor, + /** + * Register Broadcast Callbacks to track its state and receivers + * + * @param executor Executor object for callback + * @param callback Callback object to be registered + */ + public void registerServiceCallBack( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcast.Callback callback) { if (mServiceBroadcast == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d(TAG, "registerServiceCallBack failed, the BluetoothLeBroadcast is null."); + mCachedBroadcastCallbackExecutorMap.putIfAbsent(callback, executor); return; } - mServiceBroadcast.registerCallback(executor, callback); + try { + mServiceBroadcast.registerCallback(executor, callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "registerServiceCallBack failed. " + e.getMessage()); + } } /** - * Register Broadcast Assistant Callbacks to track it's state and receivers + * Register Broadcast Assistant Callbacks to track its state and receivers * * @param executor Executor object for callback * @param callback Callback object to be registered */ - public void registerBroadcastAssistantCallback(@NonNull @CallbackExecutor Executor executor, + private void registerBroadcastAssistantCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mServiceBroadcastAssistant == null) { - Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null."); + Log.d( + TAG, + "registerBroadcastAssistantCallback failed, " + + "the BluetoothLeBroadcastAssistant is null."); return; } mServiceBroadcastAssistant.registerCallback(executor, callback); } + /** + * Unregister previously registered Broadcast Callbacks + * + * @param callback Callback object to be unregistered + */ public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) { + mCachedBroadcastCallbackExecutorMap.remove(callback); if (mServiceBroadcast == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d(TAG, "unregisterServiceCallBack failed, the BluetoothLeBroadcast is null."); return; } - mServiceBroadcast.unregisterCallback(callback); + try { + mServiceBroadcast.unregisterCallback(callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "unregisterServiceCallBack failed. " + e.getMessage()); + } } /** - * Unregister previousely registered Broadcast Assistant Callbacks + * Unregister previously registered Broadcast Assistant Callbacks * * @param callback Callback object to be unregistered */ - public void unregisterBroadcastAssistantCallback( + private void unregisterBroadcastAssistantCallback( @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mServiceBroadcastAssistant == null) { - Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null."); + Log.d( + TAG, + "unregisterBroadcastAssistantCallback, " + + "the BluetoothLeBroadcastAssistant is null."); return; } @@ -570,8 +744,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } private void buildContentMetadata(String language, String programInfo) { - mBluetoothLeAudioContentMetadata = mBuilder.setLanguage(language).setProgramInfo( - programInfo).build(); + mBluetoothLeAudioContentMetadata = + mBuilder.setLanguage(language).setProgramInfo(programInfo).build(); } public LocalBluetoothLeBroadcastMetadata getLocalBluetoothLeBroadcastMetaData() { @@ -600,9 +774,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return true; } - /** - * Not supported since LE Audio Broadcasts do not establish a connection. - */ + /** Not supported since LE Audio Broadcasts do not establish a connection. */ public int getConnectionStatus(BluetoothDevice device) { if (mServiceBroadcast == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -611,9 +783,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return mServiceBroadcast.getConnectionState(device); } - /** - * Not supported since LE Audio Broadcasts do not establish a connection. - */ + /** Not supported since LE Audio Broadcasts do not establish a connection. */ public List<BluetoothDevice> getConnectedDevices() { if (mServiceBroadcast == null) { return new ArrayList<BluetoothDevice>(0); @@ -622,8 +792,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return mServiceBroadcast.getConnectedDevices(); } - public @NonNull - List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { + /** Get all broadcast metadata. */ + public @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null."); return Collections.emptyList(); @@ -640,16 +810,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return !mServiceBroadcast.getAllBroadcastMetadata().isEmpty(); } - /** - * Service does not provide method to get/set policy. - */ + /** Service does not provide method to get/set policy. */ public int getConnectionPolicy(BluetoothDevice device) { return CONNECTION_POLICY_FORBIDDEN; } /** - * Service does not provide "setEnabled" method. Please use {@link #startBroadcast}, - * {@link #stopBroadcast()} or {@link #updateBroadcast(String, String)} + * Service does not provide "setEnabled" method. Please use {@link #startBroadcast}, {@link + * #stopBroadcast()} or {@link #updateBroadcast(String, String)} */ public boolean setEnabled(BluetoothDevice device, boolean enabled) { return false; @@ -683,9 +851,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } if (mServiceBroadcast != null) { try { - BluetoothAdapter.getDefaultAdapter().closeProfileProxy( - BluetoothProfile.LE_AUDIO_BROADCAST, - mServiceBroadcast); + BluetoothAdapter.getDefaultAdapter() + .closeProfileProxy(BluetoothProfile.LE_AUDIO_BROADCAST, mServiceBroadcast); mServiceBroadcast = null; } catch (Throwable t) { Log.w(TAG, "Error cleaning up LeAudio proxy", t); @@ -694,13 +861,13 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } private String getDefaultValueOfProgramInfo() { - //set the default value; + // set the default value; int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX); return BluetoothAdapter.getDefaultAdapter().getName() + UNDERLINE + postfix; } private byte[] getDefaultValueOfBroadcastCode() { - //set the default value; + // set the default value; return generateRandomPassword().getBytes(StandardCharsets.UTF_8); } @@ -708,14 +875,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (DEBUG) { Log.d(TAG, "resetCacheInfo:"); } - setAppSourceName("", /*updateContentResolver=*/ true); + setAppSourceName("", /* updateContentResolver= */ true); mBluetoothLeBroadcastMetadata = null; mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER; } private String generateRandomPassword() { String randomUUID = UUID.randomUUID().toString(); - //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + // first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx return randomUUID.substring(0, 8) + randomUUID.substring(9, 13); } @@ -752,5 +919,4 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } } } - } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java index bb103b8896fd..34008ac56042 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java @@ -39,13 +39,14 @@ import com.android.settingslib.R; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; - /** - * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app - * and the functionality of the local {@link BluetoothLeBroadcastAssistant}. - * Use the {@link BluetoothLeBroadcastAssistant.Callback} to get the result callback. + * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app and the + * functionality of the local {@link BluetoothLeBroadcastAssistant}. Use the {@link + * BluetoothLeBroadcastAssistant.Callback} to get the result callback. */ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile { private static final String TAG = "LocalBluetoothLeBroadcastAssistant"; @@ -62,58 +63,76 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; private BluetoothLeBroadcastMetadata.Builder mBuilder; private boolean mIsProfileReady; + // Cached assistant callbacks being register before service is connected. + private final Map<BluetoothLeBroadcastAssistant.Callback, Executor> mCachedCallbackExecutorMap = + new ConcurrentHashMap<>(); + + private final ServiceListener mServiceListener = + new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service connected"); + } + mService = (BluetoothLeBroadcastAssistant) proxy; + // We just bound to the service, so refresh the UI for any connected LeAudio + // devices. + List<BluetoothDevice> deviceList = mService.getConnectedDevices(); + while (!deviceList.isEmpty()) { + BluetoothDevice nextDevice = deviceList.remove(0); + CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); + // we may add a new device here, but generally this should not happen + if (device == null) { + if (DEBUG) { + Log.d( + TAG, + "LocalBluetoothLeBroadcastAssistant found new device: " + + nextDevice); + } + device = mDeviceManager.addDevice(nextDevice); + } + device.onProfileStateChanged( + LocalBluetoothLeBroadcastAssistant.this, + BluetoothProfile.STATE_CONNECTED); + device.refresh(); + } - private final ServiceListener mServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service connected"); - } - mService = (BluetoothLeBroadcastAssistant) proxy; - // We just bound to the service, so refresh the UI for any connected LeAudio devices. - List<BluetoothDevice> deviceList = mService.getConnectedDevices(); - while (!deviceList.isEmpty()) { - BluetoothDevice nextDevice = deviceList.remove(0); - CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); - // we may add a new device here, but generally this should not happen - if (device == null) { + mProfileManager.callServiceConnectedListeners(); + mIsProfileReady = true; if (DEBUG) { - Log.d(TAG, "LocalBluetoothLeBroadcastAssistant found new device: " - + nextDevice); + Log.d( + TAG, + "onServiceConnected, register mCachedCallbackExecutorMap = " + + mCachedCallbackExecutorMap); } - device = mDeviceManager.addDevice(nextDevice); + mCachedCallbackExecutorMap.forEach( + (callback, executor) -> registerServiceCallBack(executor, callback)); } - device.onProfileStateChanged(LocalBluetoothLeBroadcastAssistant.this, - BluetoothProfile.STATE_CONNECTED); - device.refresh(); - } - mProfileManager.callServiceConnectedListeners(); - mIsProfileReady = true; - } - - @Override - public void onServiceDisconnected(int profile) { - if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) { - Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT"); - return; - } - if (DEBUG) { - Log.d(TAG, "Bluetooth service disconnected"); - } - mProfileManager.callServiceDisconnectedListeners(); - mIsProfileReady = false; - } - }; + @Override + public void onServiceDisconnected(int profile) { + if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) { + Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT"); + return; + } + if (DEBUG) { + Log.d(TAG, "Bluetooth service disconnected"); + } + mProfileManager.callServiceDisconnectedListeners(); + mIsProfileReady = false; + mCachedCallbackExecutorMap.clear(); + } + }; - public LocalBluetoothLeBroadcastAssistant(Context context, + public LocalBluetoothLeBroadcastAssistant( + Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager) { mProfileManager = profileManager; mDeviceManager = deviceManager; - BluetoothAdapter.getDefaultAdapter(). - getProfileProxy(context, mServiceListener, - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + BluetoothAdapter.getDefaultAdapter() + .getProfileProxy( + context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); mBuilder = new BluetoothLeBroadcastMetadata.Builder(); } @@ -123,11 +142,11 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * @param sink Broadcast Sink to which the Broadcast Source should be added * @param metadata Broadcast Source metadata to be added to the Broadcast Sink * @param isGroupOp {@code true} if Application wants to perform this operation for all - * coordinated set members throughout this session. Otherwise, caller - * would have to add, modify, and remove individual set members. + * coordinated set members throughout this session. Otherwise, caller would have to add, + * modify, and remove individual set members. */ - public void addSource(BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata, - boolean isGroupOp) { + public void addSource( + BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata, boolean isGroupOp) { if (mService == null) { Log.d(TAG, "The BluetoothLeBroadcastAssistant is null"); return; @@ -140,36 +159,55 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * the qr code string. * * @param sink Broadcast Sink to which the Broadcast Source should be added - * @param sourceAddressType hardware MAC Address of the device. See - * {@link BluetoothDevice.AddressType}. + * @param sourceAddressType hardware MAC Address of the device. See {@link + * BluetoothDevice.AddressType}. * @param presentationDelayMicros presentation delay of this Broadcast Source in microseconds. * @param sourceAdvertisingSid 1-byte long Advertising_SID of the Broadcast Source. * @param broadcastId 3-byte long Broadcast_ID of the Broadcast Source. - * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, - * {@link BluetoothLeBroadcastMetadata#PA_SYNC_INTERVAL_UNKNOWN} if - * unknown. + * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, {@link + * BluetoothLeBroadcastMetadata#PA_SYNC_INTERVAL_UNKNOWN} if unknown. * @param isEncrypted whether the Broadcast Source is encrypted. * @param broadcastCode Broadcast Code for this Broadcast Source, null if code is not required. * @param sourceDevice source advertiser address. * @param isGroupOp {@code true} if Application wants to perform this operation for all - * coordinated set members throughout this session. Otherwise, caller - * would have to add, modify, and remove individual set members. + * coordinated set members throughout this session. Otherwise, caller would have to add, + * modify, and remove individual set members. */ - public void addSource(@NonNull BluetoothDevice sink, int sourceAddressType, - int presentationDelayMicros, int sourceAdvertisingSid, int broadcastId, - int paSyncInterval, boolean isEncrypted, byte[] broadcastCode, - BluetoothDevice sourceDevice, boolean isGroupOp) { + public void addSource( + @NonNull BluetoothDevice sink, + int sourceAddressType, + int presentationDelayMicros, + int sourceAdvertisingSid, + int broadcastId, + int paSyncInterval, + boolean isEncrypted, + byte[] broadcastCode, + BluetoothDevice sourceDevice, + boolean isGroupOp) { if (DEBUG) { Log.d(TAG, "addSource()"); } - buildMetadata(sourceAddressType, presentationDelayMicros, sourceAdvertisingSid, broadcastId, - paSyncInterval, isEncrypted, broadcastCode, sourceDevice); + buildMetadata( + sourceAddressType, + presentationDelayMicros, + sourceAdvertisingSid, + broadcastId, + paSyncInterval, + isEncrypted, + broadcastCode, + sourceDevice); addSource(sink, mBluetoothLeBroadcastMetadata, isGroupOp); } - private void buildMetadata(int sourceAddressType, int presentationDelayMicros, - int sourceAdvertisingSid, int broadcastId, int paSyncInterval, boolean isEncrypted, - byte[] broadcastCode, BluetoothDevice sourceDevice) { + private void buildMetadata( + int sourceAddressType, + int presentationDelayMicros, + int sourceAdvertisingSid, + int broadcastId, + int paSyncInterval, + boolean isEncrypted, + byte[] broadcastCode, + BluetoothDevice sourceDevice) { mBluetoothLeBroadcastMetadata = mBuilder.setSourceDevice(sourceDevice, sourceAddressType) .setSourceAdvertisingSid(sourceAdvertisingSid) @@ -223,10 +261,10 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile /** * Stops an ongoing search for nearby Broadcast Sources. * - * On success, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopped(int)} will be - * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}. - * On failure, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopFailed(int)} will be - * called with reason code + * <p>On success, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopped(int)} will be + * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}. On failure, + * {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopFailed(int)} will be called with + * reason code * * @throws IllegalStateException if callback was not registered */ @@ -245,8 +283,8 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * Get information about all Broadcast Sources that a Broadcast Sink knows about. * * @param sink Broadcast Sink from which to get all Broadcast Sources - * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} - * stored in the Broadcast Sink + * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} stored + * in the Broadcast Sink * @throws NullPointerException when <var>sink</var> is null */ public @NonNull List<BluetoothLeBroadcastReceiveState> getAllSources( @@ -261,24 +299,50 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile return mService.getAllSources(sink); } - public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor, + /** + * Register Broadcast Assistant Callbacks to track its state and receivers + * + * @param executor Executor object for callback + * @param callback Callback object to be registered + */ + public void registerServiceCallBack( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mService == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d( + TAG, + "registerServiceCallBack failed, the BluetoothLeBroadcastAssistant is null."); + mCachedCallbackExecutorMap.putIfAbsent(callback, executor); return; } - mService.registerCallback(executor, callback); + try { + mService.registerCallback(executor, callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "registerServiceCallBack failed. " + e.getMessage()); + } } + /** + * Unregister previously registered Broadcast Assistant Callbacks + * + * @param callback Callback object to be unregistered + */ public void unregisterServiceCallBack( @NonNull BluetoothLeBroadcastAssistant.Callback callback) { + mCachedCallbackExecutorMap.remove(callback); if (mService == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d( + TAG, + "unregisterServiceCallBack failed, the BluetoothLeBroadcastAssistant is null."); return; } - mService.unregisterCallback(callback); + try { + mService.unregisterCallback(callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "unregisterServiceCallBack failed. " + e.getMessage()); + } } public boolean isProfileReady() { @@ -310,9 +374,11 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile return new ArrayList<BluetoothDevice>(0); } return mService.getDevicesMatchingConnectionStates( - new int[]{BluetoothProfile.STATE_CONNECTED, - BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTING}); + new int[] { + BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING + }); } public boolean isEnabled(BluetoothDevice device) { @@ -373,9 +439,8 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile } if (mService != null) { try { - BluetoothAdapter.getDefaultAdapter().closeProfileProxy( - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, - mService); + BluetoothAdapter.getDefaultAdapter() + .closeProfileProxy(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, mService); mService = null; } catch (Throwable t) { Log.w(TAG, "Error cleaning up LeAudio proxy", t); 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/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt index 914e5f2c17bf..fd04b5ee0d9c 100644 --- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -51,6 +51,7 @@ object ComposeFacade : BaseComposeFacade { activity: ComponentActivity, viewModel: BaseCommunalViewModel, onOpenWidgetPicker: () -> Unit, + onEditDone: () -> Unit, ) { throwComposeUnavailableError() } diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt index 59bd95bd9027..5055ee1d73f6 100644 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -66,12 +66,14 @@ object ComposeFacade : BaseComposeFacade { activity: ComponentActivity, viewModel: BaseCommunalViewModel, onOpenWidgetPicker: () -> Unit, + onEditDone: () -> Unit, ) { activity.setContent { PlatformTheme { CommunalHub( viewModel = viewModel, onOpenWidgetPicker = onOpenWidgetPicker, + onEditDone = onEditDone, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index e8ecd3a66186..2a9cf0fdc507 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -25,10 +25,12 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan @@ -36,23 +38,41 @@ import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +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.OutlinedButton +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.layout.positionInWindow +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel @@ -66,21 +86,38 @@ fun CommunalHub( modifier: Modifier = Modifier, viewModel: BaseCommunalViewModel, onOpenWidgetPicker: (() -> Unit)? = null, + onEditDone: (() -> Unit)? = null, ) { val communalContent by viewModel.communalContent.collectAsState(initial = emptyList()) + var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } + var toolbarSize: IntSize? by remember { mutableStateOf(null) } + var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } + var isDraggingToRemove by remember { mutableStateOf(false) } + Box( modifier = modifier.fillMaxSize().background(Color.White), ) { CommunalHubLazyGrid( - modifier = Modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart), + modifier = Modifier.align(Alignment.CenterStart), communalContent = communalContent, - isEditMode = viewModel.isEditMode, viewModel = viewModel, - ) - if (viewModel.isEditMode && onOpenWidgetPicker != null) { - IconButton(onClick = onOpenWidgetPicker) { - Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) + contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize), + setGridCoordinates = { gridCoordinates = it }, + updateDragPositionForRemove = { + isDraggingToRemove = + checkForDraggingToRemove(it, removeButtonCoordinates, gridCoordinates) + isDraggingToRemove } + ) + + if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) { + Toolbar( + isDraggingToRemove = isDraggingToRemove, + setToolbarSize = { toolbarSize = it }, + setRemoveButtonCoordinates = { removeButtonCoordinates = it }, + onEditDone = onEditDone, + onOpenWidgetPicker = onOpenWidgetPicker, + ) } else { IconButton(onClick = viewModel::onOpenWidgetEditor) { Icon(Icons.Default.Edit, stringResource(R.string.button_to_open_widget_editor)) @@ -103,25 +140,38 @@ fun CommunalHub( @Composable private fun CommunalHubLazyGrid( communalContent: List<CommunalContentModel>, - isEditMode: Boolean, viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier, + contentPadding: PaddingValues, + setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit, + updateDragPositionForRemove: (offset: Offset) -> Boolean, ) { var gridModifier = modifier val gridState = rememberLazyGridState() var list = communalContent var dragDropState: GridDragDropState? = null - if (isEditMode && viewModel is CommunalEditModeViewModel) { + if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) { val contentListState = rememberContentListState(communalContent, viewModel) list = contentListState.list - dragDropState = rememberGridDragDropState(gridState, contentListState) - gridModifier = gridModifier.dragContainer(dragDropState) + dragDropState = + rememberGridDragDropState( + gridState = gridState, + contentListState = contentListState, + updateDragPositionForRemove = updateDragPositionForRemove + ) + gridModifier = + gridModifier + .fillMaxSize() + .dragContainer(dragDropState, beforeContentPadding(contentPadding)) + .onGloballyPositioned { setGridCoordinates(it) } + } else { + gridModifier = gridModifier.height(Dimensions.GridHeight) } LazyHorizontalGrid( modifier = gridModifier, state = gridState, rows = GridCells.Fixed(CommunalContentSize.FULL.span), - contentPadding = PaddingValues(horizontal = Dimensions.Spacing), + contentPadding = contentPadding, horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing), verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing), ) { @@ -130,19 +180,18 @@ private fun CommunalHubLazyGrid( key = { index -> list[index].key }, span = { index -> GridItemSpan(list[index].size.span) }, ) { index -> - val cardModifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth) + val cardModifier = Modifier.width(Dimensions.CardWidth) val size = SizeF( Dimensions.CardWidth.value, list[index].size.dp().value, ) - if (isEditMode && dragDropState != null) { + if (viewModel.isEditMode && dragDropState != null) { DraggableItem(dragDropState = dragDropState, enabled = true, index = index) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 1.dp) CommunalContent( modifier = cardModifier, - deleteOnClick = viewModel::onDeleteWidget, elevation = elevation, model = list[index], viewModel = viewModel, @@ -161,6 +210,95 @@ private fun CommunalHubLazyGrid( } } +/** + * Toolbar that contains action buttons to + * 1) open the widget picker + * 2) remove a widget from the grid and + * 3) exit the edit mode. + */ +@Composable +private fun Toolbar( + isDraggingToRemove: Boolean, + setToolbarSize: (toolbarSize: IntSize) -> Unit, + setRemoveButtonCoordinates: (coordinates: LayoutCoordinates) -> Unit, + onOpenWidgetPicker: () -> Unit, + onEditDone: () -> Unit, +) { + Row( + modifier = + Modifier.fillMaxWidth() + .padding( + top = Dimensions.ToolbarPaddingTop, + start = Dimensions.ToolbarPaddingHorizontal, + end = Dimensions.ToolbarPaddingHorizontal, + ) + .onSizeChanged { setToolbarSize(it) }, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + val buttonContentPadding = + PaddingValues( + vertical = Dimensions.ToolbarButtonPaddingVertical, + horizontal = Dimensions.ToolbarButtonPaddingHorizontal, + ) + val spacerModifier = Modifier.width(Dimensions.ToolbarButtonSpaceBetween) + Button( + onClick = onOpenWidgetPicker, + colors = filledSecondaryButtonColors(), + contentPadding = buttonContentPadding + ) { + Icon(Icons.Default.Add, stringResource(R.string.button_to_open_widget_editor)) + Spacer(spacerModifier) + Text( + text = stringResource(R.string.hub_mode_add_widget_button_text), + ) + } + + val buttonColors = + if (isDraggingToRemove) filledButtonColors() else ButtonDefaults.outlinedButtonColors() + OutlinedButton( + onClick = {}, + colors = buttonColors, + contentPadding = buttonContentPadding, + modifier = Modifier.onGloballyPositioned { setRemoveButtonCoordinates(it) }, + ) { + Icon(Icons.Outlined.Delete, stringResource(R.string.button_to_open_widget_editor)) + Spacer(spacerModifier) + Text( + text = stringResource(R.string.button_to_remove_widget), + ) + } + + Button( + onClick = onEditDone, + colors = filledButtonColors(), + contentPadding = buttonContentPadding + ) { + Text( + text = stringResource(R.string.hub_mode_editing_exit_button_text), + ) + } + } +} + +@Composable +private fun filledButtonColors(): ButtonColors { + val colors = LocalAndroidColorScheme.current + return ButtonDefaults.buttonColors( + containerColor = colors.primary, + contentColor = colors.onPrimary, + ) +} + +@Composable +private fun filledSecondaryButtonColors(): ButtonColors { + val colors = LocalAndroidColorScheme.current + return ButtonDefaults.buttonColors( + containerColor = colors.secondary, + contentColor = colors.onSecondary, + ) +} + @Composable private fun CommunalContent( model: CommunalContentModel, @@ -168,11 +306,9 @@ private fun CommunalContent( size: SizeF, modifier: Modifier = Modifier, elevation: Dp = 0.dp, - deleteOnClick: ((id: Int) -> Unit)? = null, ) { when (model) { - is CommunalContentModel.Widget -> - WidgetContent(model, size, elevation, deleteOnClick, modifier) + is CommunalContentModel.Widget -> WidgetContent(model, size, elevation, modifier) is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier) is CommunalContentModel.Tutorial -> TutorialContent(modifier) is CommunalContentModel.Umo -> Umo(viewModel, modifier) @@ -184,19 +320,12 @@ private fun WidgetContent( model: CommunalContentModel.Widget, size: SizeF, elevation: Dp, - deleteOnClick: ((id: Int) -> Unit)?, modifier: Modifier = Modifier, ) { - // TODO(b/309009246): update background color Card( - modifier = modifier.fillMaxSize().background(Color.White), + modifier = modifier.height(size.height.dp), elevation = CardDefaults.cardElevation(draggedElevation = elevation), ) { - if (deleteOnClick != null) { - IconButton(onClick = { deleteOnClick(model.appWidgetId) }) { - Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget)) - } - } AndroidView( modifier = modifier, factory = { context -> @@ -249,6 +378,60 @@ private fun Umo(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) ) } +/** + * Returns the `contentPadding` of the grid. Use the vertical padding to push the grid content area + * below the toolbar and let the grid take the max size. This ensures the item can be dragged + * outside the grid over the toolbar, without part of it getting clipped by the container. + */ +@Composable +private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues { + if (!isEditMode || toolbarSize == null) { + return PaddingValues(horizontal = Dimensions.Spacing) + } + val configuration = LocalConfiguration.current + val density = LocalDensity.current + val screenHeight = configuration.screenHeightDp.dp + val toolbarHeight = with(density) { Dimensions.ToolbarPaddingTop + toolbarSize.height.toDp() } + val verticalPadding = + ((screenHeight - toolbarHeight - Dimensions.GridHeight) / 2).coerceAtLeast( + Dimensions.Spacing + ) + return PaddingValues( + start = Dimensions.ToolbarPaddingHorizontal, + end = Dimensions.ToolbarPaddingHorizontal, + top = verticalPadding + toolbarHeight, + bottom = verticalPadding + ) +} + +@Composable +private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingInPx { + return with(LocalDensity.current) { + ContentPaddingInPx( + startPadding = paddingValues.calculateLeftPadding(LayoutDirection.Ltr).toPx(), + topPadding = paddingValues.calculateTopPadding().toPx() + ) + } +} + +/** + * Check whether the pointer position that the item is being dragged at is within the coordinates of + * the remove button in the toolbar. Returns true if the item is removable. + */ +private fun checkForDraggingToRemove( + offset: Offset, + removeButtonCoordinates: LayoutCoordinates?, + gridCoordinates: LayoutCoordinates?, +): Boolean { + if (removeButtonCoordinates == null || gridCoordinates == null) { + return false + } + val pointer = gridCoordinates.positionInWindow() + offset + val removeButton = removeButtonCoordinates.positionInWindow() + return pointer.x in removeButton.x..removeButton.x + removeButtonCoordinates.size.width && + pointer.y in removeButton.y..removeButton.y + removeButtonCoordinates.size.height +} + private fun CommunalContentSize.dp(): Dp { return when (this) { CommunalContentSize.FULL -> Dimensions.CardHeightFull @@ -257,6 +440,8 @@ private fun CommunalContentSize.dp(): Dp { } } +data class ContentPaddingInPx(val startPadding: Float, val topPadding: Float) + object Dimensions { val CardWidth = 464.dp val CardHeightFull = 630.dp @@ -264,4 +449,11 @@ object Dimensions { val CardHeightThird = 199.dp val GridHeight = CardHeightFull val Spacing = 16.dp + + // The sizing/padding of the toolbar in glanceable hub edit mode + val ToolbarPaddingTop = 27.dp + val ToolbarPaddingHorizontal = 16.dp + val ToolbarButtonPaddingHorizontal = 24.dp + val ToolbarButtonPaddingVertical = 16.dp + val ToolbarButtonSpaceBetween = 8.dp } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index 6cfa2f233f46..5451d0550095 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -48,12 +48,18 @@ import kotlinx.coroutines.launch @Composable fun rememberGridDragDropState( gridState: LazyGridState, - contentListState: ContentListState + contentListState: ContentListState, + updateDragPositionForRemove: (offset: Offset) -> Boolean, ): GridDragDropState { val scope = rememberCoroutineScope() val state = remember(gridState, contentListState) { - GridDragDropState(state = gridState, contentListState = contentListState, scope = scope) + GridDragDropState( + state = gridState, + contentListState = contentListState, + scope = scope, + updateDragPositionForRemove = updateDragPositionForRemove + ) } LaunchedEffect(state) { while (true) { @@ -67,23 +73,30 @@ fun rememberGridDragDropState( /** * Handles drag and drop cards in the glanceable hub. While dragging to move, other items that are * affected will dynamically get positioned and the state is tracked by [ContentListState]. When - * dragging to remove, affected cards will be moved and [ContentListState.onRemove] is called to - * remove the dragged item. On dragging ends, call [ContentListState.onSaveList] to persist the - * change. + * dragging to remove, affected cards will be moved and [updateDragPositionForRemove] is called to + * check whether the dragged item can be removed. On dragging ends, call [ContentListState.onRemove] + * to remove the dragged item if condition met and call [ContentListState.onSaveList] to persist any + * change in ordering. */ class GridDragDropState internal constructor( private val state: LazyGridState, private val contentListState: ContentListState, private val scope: CoroutineScope, + private val updateDragPositionForRemove: (offset: Offset) -> Boolean ) { var draggingItemIndex by mutableStateOf<Int?>(null) private set + var isDraggingToRemove by mutableStateOf(false) + private set + internal val scrollChannel = Channel<Float>() private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero) private var draggingItemInitialOffset by mutableStateOf(Offset.Zero) + private var dragStartPointerOffset by mutableStateOf(Offset.Zero) + internal val draggingItemOffset: Offset get() = draggingItemLayoutInfo?.let { item -> @@ -94,27 +107,36 @@ internal constructor( private val draggingItemLayoutInfo: LazyGridItemInfo? get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex } - internal fun onDragStart(offset: Offset) { + internal fun onDragStart(offset: Offset, contentOffset: Offset) { state.layoutInfo.visibleItemsInfo .firstOrNull { item -> + // grid item offset is based off grid content container so we need to deduct + // before content padding from the initial pointer position item.isEditable && - offset.x.toInt() in item.offset.x..item.offsetEnd.x && - offset.y.toInt() in item.offset.y..item.offsetEnd.y + (offset.x - contentOffset.x).toInt() in item.offset.x..item.offsetEnd.x && + (offset.y - contentOffset.y).toInt() in item.offset.y..item.offsetEnd.y } ?.apply { + dragStartPointerOffset = offset - this.offset.toOffset() draggingItemIndex = index draggingItemInitialOffset = this.offset.toOffset() } } internal fun onDragInterrupted() { - if (draggingItemIndex != null) { + draggingItemIndex?.let { + if (isDraggingToRemove) { + contentListState.onRemove(it) + isDraggingToRemove = false + updateDragPositionForRemove(Offset.Zero) + } // persist list editing changes on dragging ends contentListState.onSaveList() draggingItemIndex = null } draggingItemDraggedDelta = Offset.Zero draggingItemInitialOffset = Offset.Zero + dragStartPointerOffset = Offset.Zero } internal fun onDrag(offset: Offset) { @@ -152,18 +174,13 @@ internal constructor( contentListState.onMove(draggingItem.index, targetItem.index) } draggingItemIndex = targetItem.index + isDraggingToRemove = false } else { val overscroll = checkForOverscroll(startOffset, endOffset) if (overscroll != 0f) { scrollChannel.trySend(overscroll) } - val removeOffset = checkForRemove(startOffset) - if (removeOffset != 0f) { - draggingItemIndex?.let { - contentListState.onRemove(it) - draggingItemIndex = null - } - } + isDraggingToRemove = checkForRemove(startOffset) } } @@ -185,14 +202,11 @@ internal constructor( } } - // TODO(b/309968801): a temporary solution to decide whether to remove card when it's dragged up - // and out of grid. Once we have a taskbar, calculate the intersection of the dragged item with - // the Remove button. - private fun checkForRemove(startOffset: Offset): Float { + /** Calls the callback with the updated drag position and returns whether to remove the item. */ + private fun checkForRemove(startOffset: Offset): Boolean { return if (draggingItemDraggedDelta.y < 0) - (startOffset.y + Dimensions.CardHeightHalf.value - state.layoutInfo.viewportStartOffset) - .coerceAtMost(0f) - else 0f + updateDragPositionForRemove(startOffset + dragStartPointerOffset) + else false } } @@ -204,14 +218,22 @@ private operator fun Offset.plus(size: Size): Offset { return Offset(x + size.width, y + size.height) } -fun Modifier.dragContainer(dragDropState: GridDragDropState): Modifier { - return pointerInput(dragDropState) { +fun Modifier.dragContainer( + dragDropState: GridDragDropState, + beforeContentPadding: ContentPaddingInPx +): Modifier { + return pointerInput(dragDropState, beforeContentPadding) { detectDragGesturesAfterLongPress( onDrag = { change, offset -> change.consume() dragDropState.onDrag(offset = offset) }, - onDragStart = { offset -> dragDropState.onDragStart(offset) }, + onDragStart = { offset -> + dragDropState.onDragStart( + offset, + Offset(beforeContentPadding.startPadding, beforeContentPadding.topPadding) + ) + }, onDragEnd = { dragDropState.onDragInterrupted() }, onDragCancel = { dragDropState.onDragInterrupted() } ) @@ -237,6 +259,7 @@ fun LazyGridItemScope.DraggableItem( Modifier.zIndex(1f).graphicsLayer { translationX = dragDropState.draggingItemOffset.x translationY = dragDropState.draggingItemOffset.y + alpha = if (dragDropState.isDraggingToRemove) 0.5f else 1f } } else { Modifier.animateItemPlacement() 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 009f8bb38e61..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,12 +174,8 @@ private fun <T> computeValue( lerp: (T, T, Float) -> T, canOverflow: Boolean, ): T { - val state = layoutImpl.state.transitionState - if ( - state !is TransitionState.Transition || - state.fromScene == state.toScene || - !layoutImpl.isTransitionReady(state) - ) { + val transition = layoutImpl.state.currentTransition + if (transition == null || !layoutImpl.isTransitionReady(transition)) { return sharedValue.value } @@ -195,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 199832bc4ab6..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,50 +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 -> { - if (state.toScene == state.fromScene) { - // Same as idle. - animate(layoutImpl, target) - return - } - // 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 @@ -95,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) { @@ -125,7 +117,6 @@ private fun CoroutineScope.animate( currentScene = target, isInitiatedByUserInput = isUserInput, isUserInputOngoing = false, - animatable = animatable, ) } else { OneOffTransition( @@ -134,32 +125,46 @@ 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) } } private class OneOffTransition( - override val fromScene: SceneKey, - override val toScene: SceneKey, + fromScene: SceneKey, + toScene: SceneKey, override val currentScene: SceneKey, override val isInitiatedByUserInput: Boolean, override val isUserInputOngoing: Boolean, - private val animatable: Animatable<Float, AnimationVector1D>, -) : TransitionState.Transition { +) : 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 31604a6817f0..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,27 +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 || - state.fromScene == state.toScene || - !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, @@ -332,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) { @@ -372,13 +368,9 @@ private fun isElementOpaque( scene: Scene, sceneValues: Element.TargetValues, ): Boolean { - val state = layoutImpl.state.transitionState - - if (state !is TransitionState.Transition || state.fromScene == state.toScene) { - 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 @@ -386,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] @@ -396,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 } /** @@ -608,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 || state.fromScene == state.toScene) { - // 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] @@ -639,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). @@ -676,7 +659,7 @@ private inline fun <T> computeValue( scene, element, sceneValues, - state, + transition, idleValue, ) @@ -686,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 fa385d014ccb..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,21 +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 - if (fromScene == toScene) { - check(fromScene == scene) - return true - } + 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) @@ -185,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/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt index 1b79dbdee510..983cff83ed57 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -73,17 +73,13 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans when (val state = transitionState) { is TransitionState.Idle -> ObservableTransitionState.Idle(state.currentScene) is TransitionState.Transition -> { - if (state.fromScene == state.toScene) { - ObservableTransitionState.Idle(state.currentScene) - } else { - ObservableTransitionState.Transition( - fromScene = state.fromScene, - toScene = state.toScene, - progress = snapshotFlow { state.progress }, - isInitiatedByUserInput = state.isInitiatedByUserInput, - isUserInputOngoing = snapshotFlow { state.isUserInputOngoing }, - ) - } + ObservableTransitionState.Transition( + fromScene = state.fromScene, + toScene = state.toScene, + progress = snapshotFlow { state.progress }, + isInitiatedByUserInput = state.isInitiatedByUserInput, + isUserInputOngoing = snapshotFlow { state.isUserInputOngoing }, + ) } } } 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 03f37d0c9bda..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,39 +41,37 @@ 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 + private var _swipeTransition: SwipeTransition? = null + internal var swipeTransition: SwipeTransition + get() = _swipeTransition ?: error("SwipeTransition needs to be initialized") set(value) { - layoutImpl.state.transitionState = value + _swipeTransition = value } - internal var swipeTransition: SwipeTransition = SwipeTransition(currentScene, currentScene, 1f) - private set - 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 @@ -82,34 +80,34 @@ internal class SceneGestureHandler( private var actionDownOrRight: UserAction? = null private var actionUpOrLeftNoEdge: UserAction? = null private var actionDownOrRightNoEdge: UserAction? = null + private var upOrLeftScene: SceneKey? = null + private var downOrRightScene: SceneKey? = null internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?, overSlop: Float) { if (isDrivingTransition) { // This [transition] was already driving the animation: simply take over it. // Stop animating and start from where the current offset. swipeTransition.cancelOffsetAnimation() + updateTargetScenes(swipeTransition._fromScene) 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) - if (fromScene.upOrLeft() == null && fromScene.downOrRight() == null) { - return - } - - val (targetScene, distance) = fromScene.findTargetSceneAndDistance(overSlop) + val (targetScene, distance) = + findTargetSceneAndDistance(fromScene, overSlop, updateScenes = true) ?: return updateTransition(SwipeTransition(fromScene, targetScene, distance), force = true) } @@ -179,16 +177,21 @@ internal class SceneGestureHandler( val (fromScene, acceleratedOffset) = computeFromSceneConsideringAcceleratedSwipe(swipeTransition) - swipeTransition.dragOffset += acceleratedOffset - // Compute the target scene depending on the current offset. + val isNewFromScene = fromScene.key != swipeTransition.fromScene val (targetScene, distance) = - fromScene.findTargetSceneAndDistance(swipeTransition.dragOffset) + findTargetSceneAndDistance( + fromScene, + swipeTransition.dragOffset, + updateScenes = isNewFromScene, + ) + ?: run { + onDragStopped(delta, true) + return + } + swipeTransition.dragOffset += acceleratedOffset - // TODO(b/290184746): support long scroll A => B => C? especially for non fullscreen scenes - if ( - fromScene.key != swipeTransition.fromScene || targetScene.key != swipeTransition.toScene - ) { + if (isNewFromScene || targetScene.key != swipeTransition.toScene) { updateTransition( SwipeTransition(fromScene, targetScene, distance).apply { this.dragOffset = swipeTransition.dragOffset @@ -197,6 +200,11 @@ internal class SceneGestureHandler( } } + private fun updateTargetScenes(fromScene: Scene) { + upOrLeftScene = fromScene.upOrLeft() + downOrRightScene = fromScene.downOrRight() + } + /** * Change fromScene in the case where the user quickly swiped multiple times in the same * direction to accelerate the transition from A => B then B => C. @@ -214,37 +222,71 @@ internal class SceneGestureHandler( val absoluteDistance = swipeTransition.distance.absoluteValue // If the swipe was not committed, don't do anything. - if (fromScene == toScene || swipeTransition._currentScene != toScene) { + if (swipeTransition._currentScene != toScene) { return Pair(fromScene, 0f) } // If the offset is past the distance then let's change fromScene so that the user can swipe // to the next screen or go back to the previous one. val offset = swipeTransition.dragOffset - return if (offset <= -absoluteDistance && fromScene.upOrLeft() == toScene.key) { + return if (offset <= -absoluteDistance && upOrLeftScene == toScene.key) { Pair(toScene, absoluteDistance) - } else if (offset >= absoluteDistance && fromScene.downOrRight() == toScene.key) { + } else if (offset >= absoluteDistance && downOrRightScene == toScene.key) { Pair(toScene, -absoluteDistance) } else { Pair(fromScene, 0f) } } - // TODO(b/290184746): there are two bugs here: - // 1. if both upOrLeft and downOrRight become `null` during a transition this will crash - // 2. if one of them changes during a transition, the transition will jump cut to the new target - private inline fun Scene.findTargetSceneAndDistance( - directionOffset: Float - ): Pair<Scene, Float> { - val upOrLeft = upOrLeft() - val downOrRight = downOrRight() - val absoluteDistance = getAbsoluteDistance() + /** + * Returns the target scene and distance from [fromScene] in the direction [directionOffset]. + * + * @param fromScene the scene from which we look for the target + * @param directionOffset signed float that indicates the direction. Positive is down or right + * negative is up or left. + * @param updateScenes whether the target scenes should be updated to the current values held in + * the Scenes map. Usually we don't want to update them while doing a drag, because this could + * change the target scene (jump cutting) to a different scene, when some system state changed + * the targets the background. However, an update is needed any time we calculate the targets + * for a new fromScene. + * @return null when there are no targets in either direction. If one direction is null and you + * drag into the null direction this function will return the opposite direction, assuming + * that the users intention is to start the drag into the other direction eventually. If + * [directionOffset] is 0f and both direction are available, it will default to + * [upOrLeftScene]. + */ + private inline fun findTargetSceneAndDistance( + fromScene: Scene, + directionOffset: Float, + updateScenes: Boolean, + ): Pair<Scene, Float>? { + if (updateScenes) updateTargetScenes(fromScene) + val absoluteDistance = fromScene.getAbsoluteDistance() // Compute the target scene depending on the current offset. - return if ((directionOffset < 0f && upOrLeft != null) || downOrRight == null) { - Pair(layoutImpl.scene(upOrLeft!!), -absoluteDistance) - } else { - Pair(layoutImpl.scene(downOrRight), absoluteDistance) + return when { + upOrLeftScene == null && downOrRightScene == null -> null + (directionOffset < 0f && upOrLeftScene != null) || downOrRightScene == null -> + Pair(layoutImpl.scene(upOrLeftScene!!), -absoluteDistance) + else -> Pair(layoutImpl.scene(downOrRightScene!!), absoluteDistance) + } + } + + /** + * A strict version of [findTargetSceneAndDistance] that will return null when there is no Scene + * in [directionOffset] direction + */ + private inline fun findTargetSceneAndDistanceStrict( + fromScene: Scene, + directionOffset: Float, + ): Pair<Scene, Float>? { + val absoluteDistance = fromScene.getAbsoluteDistance() + return when { + directionOffset > 0f -> + upOrLeftScene?.let { Pair(layoutImpl.scene(it), -absoluteDistance) } + directionOffset < 0f -> + downOrRightScene?.let { Pair(layoutImpl.scene(it), absoluteDistance) } + else -> null } } @@ -311,20 +353,21 @@ internal class SceneGestureHandler( val startFromIdlePosition = swipeTransition.dragOffset == 0f if (startFromIdlePosition) { - // If there is a next scene, we start the overscroll animation. - val (targetScene, distance) = fromScene.findTargetSceneAndDistance(velocity) - val isValidTarget = distance != 0f && targetScene.key != fromScene.key - if (isValidTarget) { - updateTransition( - SwipeTransition(fromScene, targetScene, distance).apply { - _currentScene = swipeTransition._currentScene + // If there is a target scene, we start the overscroll animation. + val (targetScene, distance) = + findTargetSceneAndDistanceStrict(fromScene, velocity) + ?: run { + // We will not animate + layoutState.finishTransition(swipeTransition, idleScene = fromScene.key) + return } - ) - animateTo(targetScene = fromScene, targetOffset = 0f) - } else { - // We will not animate - transitionState = TransitionState.Idle(fromScene.key) - } + + updateTransition( + SwipeTransition(fromScene, targetScene, distance).apply { + _currentScene = swipeTransition._currentScene + } + ) + animateTo(targetScene = fromScene, targetOffset = 0f) } else { // We were between two scenes: animate to the initial scene. animateTo(targetScene = fromScene, targetOffset = 0f) @@ -390,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) } } } @@ -410,15 +446,11 @@ internal class SceneGestureHandler( * above or to the left of [toScene]. */ val distance: Float - ) : TransitionState.Transition { + ) : TransitionState.Transition(_fromScene.key, _toScene.key) { var _currentScene by mutableStateOf(_fromScene) override val currentScene: SceneKey get() = _currentScene.key - override val fromScene: SceneKey = _fromScene.key - - override val toScene: SceneKey = _toScene.key - override val progress: Float get() { val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset @@ -494,10 +526,14 @@ private class SceneDraggableHandler( } internal class SceneNestedScrollHandler( - private val gestureHandler: SceneGestureHandler, - private val topOrLeftBehavior: NestedScrollBehavior, - private val bottomOrRightBehavior: NestedScrollBehavior, + 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 { @@ -508,7 +544,7 @@ internal class SceneNestedScrollHandler( val actionUpOrLeft = Swipe( direction = - when (gestureHandler.orientation) { + when (orientation) { Orientation.Horizontal -> SwipeDirection.Left Orientation.Vertical -> SwipeDirection.Up }, @@ -518,7 +554,7 @@ internal class SceneNestedScrollHandler( val actionDownOrRight = Swipe( direction = - when (gestureHandler.orientation) { + when (orientation) { Orientation.Horizontal -> SwipeDirection.Right Orientation.Vertical -> SwipeDirection.Down }, @@ -526,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] @@ -537,7 +573,7 @@ internal class SceneNestedScrollHandler( } return PriorityNestedScrollConnection( - orientation = gestureHandler.orientation, + orientation = orientation, canStartPreScroll = { offsetAvailable, offsetBeforeStart -> canChangeScene = offsetBeforeStart == 0f @@ -545,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 @@ -554,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 02ddccbc051b..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 || state.fromScene == state.toScene) { + 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,16 +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) || - state.fromScene == state.toScene - ) { - 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 f48e9147eef4..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,36 +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 - - // TODO(b/310915136): Remove this check. - if (transition.fromScene == transition.toScene) { - 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 @@ -71,32 +67,77 @@ sealed interface TransitionState { /** No transition/animation is currently running. */ data class Idle(override val currentScene: SceneKey) : TransitionState - /** - * There is a transition animating between two scenes. - * - * Important note: [fromScene] and [toScene] might be the same, in which case this [Transition] - * should be treated the same as [Idle]. This is designed on purpose so that a [Transition] can - * be started without knowing in advance where it is transitioning to, making the logic of - * [swipeToScene] easier to reason about. - */ - interface Transition : TransitionState { - /** The scene this transition is starting from. */ - val fromScene: SceneKey + /** There is a transition animating between two scenes. */ + abstract class Transition( + /** The scene this transition is starting from. Can't be the same as toScene */ + val fromScene: SceneKey, - /** The scene this transition is going to. */ + /** The scene this transition is going to. Can't be the same as fromScene */ val toScene: SceneKey + ) : TransitionState { + + init { + check(fromScene != toScene) + } /** * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or * when flinging quickly during a swipe gesture. */ - val progress: Float + abstract val progress: Float /** Whether the transition was triggered by user input rather than being programmatic. */ - val isInitiatedByUserInput: Boolean + abstract val isInitiatedByUserInput: Boolean /** Whether user input is currently driving the transition. */ - val isUserInputOngoing: Boolean + 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 34afc4c91d4c..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,19 +55,25 @@ 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) + + val mutableUserActionsB: MutableMap<UserAction, SceneKey> = + mutableMapOf(Swipe.Up to SceneC, Swipe.Down to SceneA) private val scenesBuilder: SceneTransitionLayoutScope.() -> Unit = { scene( key = SceneA, - userActions = mapOf(Swipe.Up to SceneB, Swipe.Down to SceneC), + userActions = mutableUserActionsA, ) { Text("SceneA") } scene( key = SceneB, - userActions = mapOf(Swipe.Up to SceneC, Swipe.Down to SceneA), + userActions = mutableUserActionsB, ) { Text("SceneB") } @@ -87,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, ) @@ -412,6 +406,70 @@ class SceneGestureHandlerTest { } @Test + fun onAccelaratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest { + draggable.onDragStarted() + draggable.onDelta(up(0.2f)) + + draggable.onDelta(up(0.2f)) + draggable.onDragStopped(velocity = -velocityThreshold) + assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB) + + mutableUserActionsA.remove(Swipe.Up) + mutableUserActionsA.remove(Swipe.Down) + mutableUserActionsB.remove(Swipe.Up) + mutableUserActionsB.remove(Swipe.Down) + + // start accelaratedScroll and scroll over to B -> null + draggable.onDragStarted() + draggable.onDelta(up(0.5f)) + draggable.onDelta(up(0.5f)) + + // here onDragStopped is already triggered, but subsequent onDelta/onDragStopped calls may + // still be called. Make sure that they don't crash or change the scene + draggable.onDelta(up(0.5f)) + draggable.onDragStopped(0f) + + advanceUntilIdle() + assertIdle(SceneB) + + // These events can still come in after the animation has settled + draggable.onDelta(up(0.5f)) + draggable.onDragStopped(0f) + assertIdle(SceneB) + } + + @Test + fun onDragTargetsChanged_targetStaysTheSame() = runGestureTest { + draggable.onDragStarted(up(0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f) + + mutableUserActionsA[Swipe.Up] = SceneC + draggable.onDelta(up(0.1f)) + // target stays B even though UserActions changed + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.2f) + draggable.onDragStopped(down(0.1f)) + advanceUntilIdle() + + // now target changed to C for new drag + draggable.onDragStarted(up(0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.1f) + } + + @Test + fun onDragTargetsChanged_targetsChangeWhenStartingNewDrag() = runGestureTest { + draggable.onDragStarted(up(0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f) + + mutableUserActionsA[Swipe.Up] = SceneC + draggable.onDelta(up(0.1f)) + draggable.onDragStopped(down(0.1f)) + + // now target changed to C for new drag that started before previous drag settled to Idle + draggable.onDragStarted(up(0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.3f) + } + + @Test fun startGestureDuringAnimatingOffset_shouldImmediatelyStopTheAnimation() = runGestureTest { draggable.onDragStarted() assertTransition(currentScene = SceneA) 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 94c51ca50667..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 @@ -39,21 +39,9 @@ class SceneTransitionLayoutStateTest { } @Test - fun isTransitioningTo_fromSceneEqualToToScene() { - val state = SceneTransitionLayoutState(TestScenes.SceneA) - state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneA) - - assertThat(state.isTransitioning()).isFalse() - assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse() - assertThat(state.isTransitioning(to = TestScenes.SceneB)).isFalse() - assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)) - .isFalse() - } - - @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() @@ -64,10 +52,8 @@ class SceneTransitionLayoutStateTest { } private fun transition(from: SceneKey, to: SceneKey): TransitionState.Transition { - return object : TransitionState.Transition { + return object : TransitionState.Transition(from, to) { override val currentScene: SceneKey = from - override val fromScene: SceneKey = from - override val toScene: SceneKey = to override val progress: Float = 0f override val isInitiatedByUserInput: Boolean = false override val isUserInputOngoing: Boolean = false 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/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 16cfa2398fd5..1f8e29adc983 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -161,7 +161,7 @@ class CommunalInteractorTest : SysuiTestCase() { whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java)) val targets = listOf(target1, target2, target3) - smartspaceRepository.setLockscreenSmartspaceTargets(targets) + smartspaceRepository.setCommunalSmartspaceTargets(targets) val smartspaceContent by collectLastValue(underTest.smartspaceContent) assertThat(smartspaceContent?.size).isEqualTo(1) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index 8896e6e64bd9..314dfdfd6f2a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -116,7 +116,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { whenever(target.smartspaceTargetId).thenReturn("target") whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER) whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java)) - smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target)) + smartspaceRepository.setCommunalSmartspaceTargets(listOf(target)) // Media playing. mediaRepository.mediaPlaying.value = true diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 7fbcae0d8986..8a71168324aa 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -135,7 +135,7 @@ class CommunalViewModelTest : SysuiTestCase() { whenever(target.smartspaceTargetId).thenReturn("target") whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER) whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java)) - smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target)) + smartspaceRepository.setCommunalSmartspaceTargets(listOf(target)) // Media playing. mediaRepository.mediaPlaying.value = true diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 45aca175657e..d6d5b23a311d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -827,7 +827,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun isAuthenticatedIsResetToFalseWhenKeyguardIsGoingAway() = + fun isAuthenticatedIsResetToFalseWhenDeviceStartsGoingToSleep() = testScope.runTest { initCollectors() allPreconditionsToRunFaceAuthAreTrue() @@ -840,13 +840,13 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(authenticated()).isTrue() - keyguardRepository.setKeyguardGoingAway(true) + powerInteractor.setAsleepForTest() assertThat(authenticated()).isFalse() } @Test - fun isAuthenticatedIsResetToFalseWhenDeviceStartsGoingToSleep() = + fun isAuthenticatedIsResetToFalseWhenDeviceGoesToSleep() = testScope.runTest { initCollectors() allPreconditionsToRunFaceAuthAreTrue() @@ -865,7 +865,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun isAuthenticatedIsResetToFalseWhenDeviceGoesToSleep() = + fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() = testScope.runTest { initCollectors() allPreconditionsToRunFaceAuthAreTrue() @@ -878,13 +878,16 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(authenticated()).isTrue() - powerInteractor.setAsleepForTest() + fakeUserRepository.setSelectedUserInfo( + primaryUser, + SelectionStatus.SELECTION_IN_PROGRESS + ) assertThat(authenticated()).isFalse() } @Test - fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() = + fun isAuthenticatedIsResetToFalseWhenKeyguardDoneAnimationsFinished() = testScope.runTest { initCollectors() allPreconditionsToRunFaceAuthAreTrue() @@ -897,10 +900,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(authenticated()).isTrue() - fakeUserRepository.setSelectedUserInfo( - primaryUser, - SelectionStatus.SELECTION_IN_PROGRESS - ) + keyguardRepository.keyguardDoneAnimationsFinished() assertThat(authenticated()).isFalse() } 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/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt new file mode 100644 index 000000000000..e8504563b4ae --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt @@ -0,0 +1,40 @@ +package com.android.systemui.keyguard.ui.preview + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class KeyguardRemotePreviewManagerTest : SysuiTestCase() { + + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + @Test + fun onDestroy_clearsReferencesToRenderer() = + testScope.runTest { + val renderer = mock<KeyguardPreviewRenderer>() + val onDestroy: (PreviewLifecycleObserver) -> Unit = {} + + val observer = PreviewLifecycleObserver(this, testDispatcher, renderer, onDestroy) + + // Precondition check. + assertThat(observer.renderer).isNotNull() + assertThat(observer.onDestroy).isNotNull() + + observer.onDestroy() + + // The verification checks renderer/requestDestruction lambda because they-re + // non-singletons which can't leak KeyguardPreviewRenderer. + assertThat(observer.renderer).isNull() + assertThat(observer.onDestroy).isNull() + } +} 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/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt new file mode 100644 index 000000000000..ef2046d85a14 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt @@ -0,0 +1,172 @@ +/* + * 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.smartspace + +import android.app.smartspace.SmartspaceManager +import android.app.smartspace.SmartspaceSession +import android.app.smartspace.SmartspaceTarget +import android.content.Context +import android.graphics.drawable.Drawable +import android.testing.TestableLooper +import android.view.View +import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.smartspace.CommunalSmartspaceController +import com.android.systemui.plugins.BcSmartspaceConfigPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.util.concurrency.Execution +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import java.util.Optional +import java.util.concurrent.Executor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class CommunalSmartspaceControllerTest : SysuiTestCase() { + @Mock private lateinit var smartspaceManager: SmartspaceManager + + @Mock private lateinit var execution: Execution + + @Mock private lateinit var uiExecutor: Executor + + @Mock private lateinit var targetFilter: SmartspaceTargetFilter + + @Mock private lateinit var plugin: BcSmartspaceDataPlugin + + @Mock private lateinit var precondition: SmartspacePrecondition + + @Mock private lateinit var listener: BcSmartspaceDataPlugin.SmartspaceTargetListener + + @Mock private lateinit var session: SmartspaceSession + + private lateinit var controller: CommunalSmartspaceController + + // TODO(b/272811280): Remove usage of real view + private val fakeParent = FrameLayout(context) + + /** + * A class which implements SmartspaceView and extends View. This is mocked to provide the right + * object inheritance and interface implementation used in CommunalSmartspaceController + */ + private class TestView(context: Context?) : View(context), SmartspaceView { + override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {} + + override fun registerConfigProvider(plugin: BcSmartspaceConfigPlugin?) {} + + override fun setPrimaryTextColor(color: Int) {} + + override fun setUiSurface(uiSurface: String) {} + + override fun setDozeAmount(amount: Float) {} + + override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {} + + override fun setFalsingManager(falsingManager: FalsingManager?) {} + + override fun setDnd(image: Drawable?, description: String?) {} + + override fun setNextAlarm(image: Drawable?, description: String?) {} + + override fun setMediaTarget(target: SmartspaceTarget?) {} + + override fun getSelectedPage(): Int { + return 0 + } + + override fun getCurrentCardTopPadding(): Int { + return 0 + } + } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session) + + controller = + CommunalSmartspaceController( + context, + smartspaceManager, + execution, + uiExecutor, + precondition, + Optional.of(targetFilter), + Optional.of(plugin) + ) + } + + /** Ensures smartspace session begins on a listener only flow. */ + @Test + fun testConnectOnListen() { + `when`(precondition.conditionsMet()).thenReturn(true) + controller.addListener(listener) + + verify(smartspaceManager).createSmartspaceSession(any()) + + var targetListener = + withArgCaptor<SmartspaceSession.OnTargetsAvailableListener> { + verify(session).addOnTargetsAvailableListener(any(), capture()) + } + + `when`(targetFilter.filterSmartspaceTarget(any())).thenReturn(true) + + var target = Mockito.mock(SmartspaceTarget::class.java) + targetListener.onTargetsAvailable(listOf(target)) + + var targets = + withArgCaptor<List<SmartspaceTarget>> { verify(plugin).onTargetsAvailable(capture()) } + + assertThat(targets.contains(target)).isTrue() + + controller.removeListener(listener) + + verify(session).close() + } + + /** + * Ensures session is closed and weather plugin unregisters the notifier when weather smartspace + * view is detached. + */ + @Test + fun testDisconnect_emitsEmptyListAndRemovesNotifier() { + `when`(precondition.conditionsMet()).thenReturn(true) + controller.addListener(listener) + + verify(smartspaceManager).createSmartspaceSession(any()) + + controller.removeListener(listener) + + verify(session).close() + + // And the listener receives an empty list of targets and unregisters the notifier + verify(plugin).onTargetsAvailable(emptyList()) + verify(plugin).registerSmartspaceEventNotifier(null) + } +} diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index 64c0f99f4ba7..c99cb39f91bf 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -44,6 +44,7 @@ public interface BcSmartspaceDataPlugin extends Plugin { String UI_SURFACE_HOME_SCREEN = "home"; String UI_SURFACE_MEDIA = "media_data_manager"; String UI_SURFACE_DREAM = "dream"; + String UI_SURFACE_GLANCEABLE_HUB = "glanceable_hub"; String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA"; int VERSION = 1; 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/drawable/arrow_pointing_down.xml b/packages/SystemUI/res/drawable/arrow_pointing_down.xml new file mode 100644 index 000000000000..be39683cd78d --- /dev/null +++ b/packages/SystemUI/res/drawable/arrow_pointing_down.xml @@ -0,0 +1,26 @@ +<!-- + ~ 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/record_issue_dialog.xml b/packages/SystemUI/res/layout/record_issue_dialog.xml new file mode 100644 index 000000000000..53ad9f157a2e --- /dev/null +++ b/packages/SystemUI/res/layout/record_issue_dialog.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:orientation="vertical" > + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:textAppearance="@style/TextAppearance.Dialog.Body.Message" + android:text="@string/qs_record_issue_dropdown_header" /> + + <Button + android:id="@+id/issue_type_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/qs_record_issue_dropdown_prompt" + android:lines="1" + android:drawableRight="@drawable/arrow_pointing_down" + android:layout_marginTop="@dimen/qqs_layout_margin_top" + android:focusable="false" + android:clickable="true" /> + + <!-- Screen Record Switch --> + <LinearLayout + android:id="@+id/screenrecord_switch_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/qqs_layout_margin_top" + android:orientation="horizontal"> + + <ImageView + android:layout_width="@dimen/screenrecord_option_icon_size" + android:layout_height="@dimen/screenrecord_option_icon_size" + android:layout_weight="0" + android:src="@drawable/ic_screenrecord" + app:tint="?androidprv:attr/materialColorOnSurface" + android:layout_gravity="center" + android:layout_marginEnd="@dimen/screenrecord_option_padding" /> + + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:minHeight="@dimen/screenrecord_option_icon_size" + android:layout_weight="1" + android:layout_gravity="fill_vertical" + android:gravity="center" + android:text="@string/quick_settings_screen_record_label" + android:textAppearance="@style/TextAppearance.Dialog.Body.Message" + android:importantForAccessibility="no"/> + + <Switch + android:id="@+id/screenrecord_switch" + android:layout_width="wrap_content" + android:minHeight="@dimen/screenrecord_option_icon_size" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_gravity="fill_vertical" + android:layout_weight="0" + android:contentDescription="@string/quick_settings_screen_record_label" /> + </LinearLayout> +</LinearLayout>
\ No newline at end of file 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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 78b701caa3b6..e10925d551e2 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -831,6 +831,20 @@ <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] --> <string name="qs_record_issue_stop">Stop</string> + <!-- QuickSettings: Issue Type Drop down options in Record Issue Start Dialog [CHAR LIMIT=50] --> + <string name="qs_record_issue_dropdown_header">What part of your device experience was affected?</string> + <!-- QuickSettings: Issue Type Drop down prompt in Record Issue Start Dialog [CHAR LIMIT=30] --> + <string name="qs_record_issue_dropdown_prompt">Select issue type</string> + <!-- QuickSettings: Screen record switch label in Record Issue Start Dialog [CHAR LIMIT=20] --> + <string name="qs_record_issue_dropdown_screenrecord">Screen record</string> + + <!-- QuickSettings: Issue Type Drop down choices list in Record Issue Start Dialog [CHAR LIMIT=30] --> + <string-array name="qs_record_issue_types"> + <item>Performance</item> + <item>User Interface</item> + <item>Battery</item> + </string-array> + <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] --> <string name="quick_settings_onehanded_label">One-handed mode</string> @@ -1066,9 +1080,11 @@ <!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] --> <string name="button_to_open_widget_editor">Open the widget editor</string> <!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] --> - <string name="button_to_remove_widget">Remove a widget</string> + <string name="button_to_remove_widget">Remove</string> <!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] --> - <string name="hub_mode_add_widget_button_text">Add Widget</string> + <string name="hub_mode_add_widget_button_text">Add widget</string> + <!-- Text for the button that exits the hub mode editing mode. [CHAR LIMIT=50] --> + <string name="hub_mode_editing_exit_button_text">Done</string> <!-- Related to user switcher --><skip/> 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/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java index df7182b90f12..d6a5477fd096 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java @@ -16,7 +16,6 @@ package com.android.systemui.shared.system; -import android.annotation.IntDef; import android.os.Build; import android.text.TextUtils; import android.view.View; @@ -25,59 +24,14 @@ import com.android.internal.jank.Cuj; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.jank.InteractionJankMonitor.Configuration; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - public final class InteractionJankMonitorWrapper { - // Launcher journeys. - public static final int CUJ_APP_LAUNCH_FROM_RECENTS = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS; - public static final int CUJ_APP_LAUNCH_FROM_ICON = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON; - public static final int CUJ_APP_CLOSE_TO_HOME = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME; - public static final int CUJ_APP_CLOSE_TO_HOME_FALLBACK = - Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; - public static final int CUJ_APP_CLOSE_TO_PIP = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_PIP; - public static final int CUJ_QUICK_SWITCH = Cuj.CUJ_LAUNCHER_QUICK_SWITCH; - public static final int CUJ_OPEN_ALL_APPS = Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS; - public static final int CUJ_CLOSE_ALL_APPS_SWIPE = Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE; - public static final int CUJ_CLOSE_ALL_APPS_TO_HOME = Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME; - public static final int CUJ_ALL_APPS_SCROLL = Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL; - public static final int CUJ_APP_LAUNCH_FROM_WIDGET = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET; - public static final int CUJ_SPLIT_SCREEN_ENTER = Cuj.CUJ_SPLIT_SCREEN_ENTER; - public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = - Cuj.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; - public static final int CUJ_RECENTS_SCROLLING = Cuj.CUJ_RECENTS_SCROLLING; - public static final int CUJ_APP_SWIPE_TO_RECENTS = Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS; - public static final int CUJ_OPEN_SEARCH_RESULT = Cuj.CUJ_LAUNCHER_OPEN_SEARCH_RESULT; - public static final int CUJ_LAUNCHER_UNFOLD_ANIM = Cuj.CUJ_LAUNCHER_UNFOLD_ANIM; - - @IntDef({ - CUJ_APP_LAUNCH_FROM_RECENTS, - CUJ_APP_LAUNCH_FROM_ICON, - CUJ_APP_CLOSE_TO_HOME, - CUJ_APP_CLOSE_TO_HOME_FALLBACK, - CUJ_APP_CLOSE_TO_PIP, - CUJ_QUICK_SWITCH, - CUJ_APP_LAUNCH_FROM_WIDGET, - CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION, - CUJ_RECENTS_SCROLLING, - CUJ_APP_SWIPE_TO_RECENTS, - CUJ_OPEN_ALL_APPS, - CUJ_CLOSE_ALL_APPS_SWIPE, - CUJ_CLOSE_ALL_APPS_TO_HOME, - CUJ_OPEN_SEARCH_RESULT, - CUJ_LAUNCHER_UNFOLD_ANIM, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CujType { - } - /** * Begin a trace session. * * @param v an attached view. * @param cujType the specific {@link Cuj.CujType}. */ - public static void begin(View v, @CujType int cujType) { + public static void begin(View v, @Cuj.CujType int cujType) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; InteractionJankMonitor.getInstance().begin(v, cujType); } @@ -89,7 +43,7 @@ public final class InteractionJankMonitorWrapper { * @param cujType the specific {@link Cuj.CujType}. * @param timeout duration to cancel the instrumentation in ms */ - public static void begin(View v, @CujType int cujType, long timeout) { + public static void begin(View v, @Cuj.CujType int cujType, long timeout) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; Configuration.Builder builder = Configuration.Builder.withView(cujType, v) @@ -104,7 +58,7 @@ public final class InteractionJankMonitorWrapper { * @param cujType the specific {@link Cuj.CujType}. * @param tag the tag to distinguish different flow of same type CUJ. */ - public static void begin(View v, @CujType int cujType, String tag) { + public static void begin(View v, @Cuj.CujType int cujType, String tag) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; Configuration.Builder builder = Configuration.Builder.withView(cujType, v); @@ -119,7 +73,7 @@ public final class InteractionJankMonitorWrapper { * * @param cujType the specific {@link Cuj.CujType}. */ - public static void end(@CujType int cujType) { + public static void end(@Cuj.CujType int cujType) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; InteractionJankMonitor.getInstance().end(cujType); } @@ -127,7 +81,7 @@ public final class InteractionJankMonitorWrapper { /** * Cancel the trace session. */ - public static void cancel(@CujType int cujType) { + public static void cancel(@Cuj.CujType int cujType) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; InteractionJankMonitor.getInstance().cancel(cujType); } 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/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 1a2a4253e761..e342c6bca6fa 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -122,7 +122,7 @@ constructor( if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) { flowOf(emptyList()) } else { - smartspaceRepository.lockscreenSmartspaceTargets.map { targets -> + smartspaceRepository.communalSmartspaceTargets.map { targets -> targets .filter { target -> target.featureType == SmartspaceTarget.FEATURE_TIMER && diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt new file mode 100644 index 000000000000..c5610c877f57 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt @@ -0,0 +1,195 @@ +/* + * 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.communal.smartspace + +import android.app.smartspace.SmartspaceConfig +import android.app.smartspace.SmartspaceManager +import android.app.smartspace.SmartspaceSession +import android.app.smartspace.SmartspaceTarget +import android.content.Context +import android.util.Log +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.BcSmartspaceDataPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView +import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_GLANCEABLE_HUB +import com.android.systemui.smartspace.SmartspacePrecondition +import com.android.systemui.smartspace.SmartspaceTargetFilter +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN +import com.android.systemui.util.concurrency.Execution +import java.util.Optional +import java.util.concurrent.Executor +import javax.inject.Inject +import javax.inject.Named + +/** Controller for managing the smartspace view on the dream */ +@SysUISingleton +class CommunalSmartspaceController +@Inject +constructor( + private val context: Context, + private val smartspaceManager: SmartspaceManager?, + private val execution: Execution, + @Main private val uiExecutor: Executor, + @Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition, + @Named(DREAM_SMARTSPACE_TARGET_FILTER) + private val optionalTargetFilter: Optional<SmartspaceTargetFilter>, + @Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>, +) { + companion object { + private const val TAG = "CommunalSmartspaceCtrlr" + } + + private var session: SmartspaceSession? = null + private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null) + private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null) + + // A shadow copy of listeners is maintained to track whether the session should remain open. + private var listeners = mutableSetOf<SmartspaceTargetListener>() + + private var unfilteredListeners = mutableSetOf<SmartspaceTargetListener>() + + // Smartspace can be used on multiple displays, such as when the user casts their screen + private var smartspaceViews = mutableSetOf<SmartspaceView>() + + var preconditionListener = + object : SmartspacePrecondition.Listener { + override fun onCriteriaChanged() { + reloadSmartspace() + } + } + + init { + precondition.addListener(preconditionListener) + } + + var filterListener = + object : SmartspaceTargetFilter.Listener { + override fun onCriteriaChanged() { + reloadSmartspace() + } + } + + init { + targetFilter?.addListener(filterListener) + } + + private val sessionListener = + SmartspaceSession.OnTargetsAvailableListener { targets -> + execution.assertIsMainThread() + + val filteredTargets = + targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true } + plugin?.onTargetsAvailable(filteredTargets) + } + + private fun hasActiveSessionListeners(): Boolean { + return smartspaceViews.isNotEmpty() || + listeners.isNotEmpty() || + unfilteredListeners.isNotEmpty() + } + + private fun connectSession() { + if (smartspaceManager == null) { + return + } + if (plugin == null) { + return + } + if (session != null || !hasActiveSessionListeners()) { + return + } + + if (!precondition.conditionsMet()) { + return + } + + val newSession = + smartspaceManager.createSmartspaceSession( + SmartspaceConfig.Builder(context, UI_SURFACE_GLANCEABLE_HUB).build() + ) + Log.d(TAG, "Starting smartspace session for dream") + newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener) + this.session = newSession + + plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } + + reloadSmartspace() + } + + /** Disconnects the smartspace view from the smartspace service and cleans up any resources. */ + private fun disconnect() { + if (hasActiveSessionListeners()) return + + execution.assertIsMainThread() + + if (session == null) { + return + } + + session?.let { + it.removeOnTargetsAvailableListener(sessionListener) + it.close() + } + + session = null + + plugin?.registerSmartspaceEventNotifier(null) + plugin?.onTargetsAvailable(emptyList()) + Log.d(TAG, "Ending smartspace session for dream") + } + + fun addListener(listener: SmartspaceTargetListener) { + addAndRegisterListener(listener, plugin) + } + + fun removeListener(listener: SmartspaceTargetListener) { + removeAndUnregisterListener(listener, plugin) + } + + private fun addAndRegisterListener( + listener: SmartspaceTargetListener, + smartspaceDataPlugin: BcSmartspaceDataPlugin? + ) { + execution.assertIsMainThread() + smartspaceDataPlugin?.registerListener(listener) + listeners.add(listener) + + connectSession() + } + + private fun removeAndUnregisterListener( + listener: SmartspaceTargetListener, + smartspaceDataPlugin: BcSmartspaceDataPlugin? + ) { + execution.assertIsMainThread() + smartspaceDataPlugin?.unregisterListener(listener) + listeners.remove(listener) + disconnect() + } + + private fun reloadSmartspace() { + session?.requestSmartspaceUpdate() + } + + private fun onTargetsAvailableUnfiltered(targets: List<SmartspaceTarget>) { + unfilteredListeners.forEach { it.onSmartspaceTargetsUpdated(targets) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 7b94fc182fe2..573a748b4290 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -76,6 +76,10 @@ constructor( Intent(applicationContext, WidgetPickerActivity::class.java) ) }, + onEditDone = { + // TODO(b/315154364): in a separate change, lock the device and transition to GH + finish() + } ) } } 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/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt index 65d44957222a..3a927396527c 100644 --- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt +++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt @@ -63,6 +63,7 @@ interface BaseComposeFacade { activity: ComponentActivity, viewModel: BaseCommunalViewModel, onOpenWidgetPicker: () -> Unit, + onEditDone: () -> Unit, ) /** Create a [View] to represent [viewModel] on screen. */ diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt index 63b01edb01fa..0daa058720ba 100644 --- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt @@ -35,23 +35,22 @@ import java.util.concurrent.Executor import javax.inject.Inject /** Dialog to select contrast options */ -class ContrastDialogDelegate @Inject constructor( - private val sysuiDialogFactory : SystemUIDialog.Factory, +class ContrastDialogDelegate +@Inject +constructor( + private val sysuiDialogFactory: SystemUIDialog.Factory, @Main private val mainExecutor: Executor, private val uiModeManager: UiModeManager, private val userTracker: UserTracker, private val secureSettings: SecureSettings, ) : SystemUIDialog.Delegate, UiModeManager.ContrastChangeListener { - override fun createDialog(): SystemUIDialog { - return sysuiDialogFactory.create(this) - } - @VisibleForTesting lateinit var contrastButtons: Map<Int, FrameLayout> lateinit var dialogView: View @VisibleForTesting var initialContrast: Float = fromContrastLevel(CONTRAST_LEVEL_STANDARD) - override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + override fun createDialog(): SystemUIDialog { + val dialog = sysuiDialogFactory.create(this) dialogView = dialog.layoutInflater.inflate(R.layout.contrast_dialog, null) with(dialog) { setView(dialogView) @@ -67,12 +66,16 @@ class ContrastDialogDelegate @Inject constructor( } setPositiveButton(com.android.settingslib.R.string.done) { _, _ -> dialog.dismiss() } } + + return dialog + } + + override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { contrastButtons = mapOf( - CONTRAST_LEVEL_STANDARD to dialogView.requireViewById( - R.id.contrast_button_standard), - CONTRAST_LEVEL_MEDIUM to dialogView.requireViewById(R.id.contrast_button_medium), - CONTRAST_LEVEL_HIGH to dialogView.requireViewById(R.id.contrast_button_high) + CONTRAST_LEVEL_STANDARD to dialog.requireViewById(R.id.contrast_button_standard), + CONTRAST_LEVEL_MEDIUM to dialog.requireViewById(R.id.contrast_button_medium), + CONTRAST_LEVEL_HIGH to dialog.requireViewById(R.id.contrast_button_high) ) contrastButtons.forEach { (contrastLevel, contrastButton) -> 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 b7260f2b2b94..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; @@ -141,6 +141,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.flags.SystemPropertiesHelper; import com.android.systemui.keyguard.dagger.KeyguardModule; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.log.SessionTracker; @@ -616,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(); @@ -1319,6 +1320,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private DeviceConfigProxy mDeviceConfig; private DozeParameters mDozeParameters; private SelectedUserInteractor mSelectedUserInteractor; + private KeyguardInteractor mKeyguardInteractor; private final KeyguardStateController mKeyguardStateController; private final KeyguardStateController.Callback mKeyguardStateControllerCallback = @@ -1400,7 +1402,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel, SystemPropertiesHelper systemPropertiesHelper, Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager, - SelectedUserInteractor selectedUserInteractor) { + SelectedUserInteractor selectedUserInteractor, + KeyguardInteractor keyguardInteractor) { mContext = context; mUserTracker = userTracker; mFalsingCollector = falsingCollector; @@ -1441,6 +1444,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, })); mDozeParameters = dozeParameters; mSelectedUserInteractor = selectedUserInteractor; + mKeyguardInteractor = keyguardInteractor; mStatusBarStateController = statusBarStateController; statusBarStateController.addCallback(this); @@ -1498,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()); } @@ -2618,6 +2622,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, setPendingLock(false); // user may have authenticated during the screen off animation handleHide(); + mKeyguardInteractor.keyguardDoneAnimationsFinished(); mUpdateMonitor.clearFingerprintRecognizedWhenKeyguardDone(currentUser); Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 331d892304b3..3925dd1620b4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -52,6 +52,7 @@ import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager; import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule; import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule; import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger; import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl; @@ -154,7 +155,8 @@ public class KeyguardModule { Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel, SystemPropertiesHelper systemPropertiesHelper, Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager, - SelectedUserInteractor selectedUserInteractor) { + SelectedUserInteractor selectedUserInteractor, + KeyguardInteractor keyguardInteractor) { return new KeyguardViewMediator( context, uiEventLogger, @@ -199,7 +201,8 @@ public class KeyguardModule { dreamingToLockscreenTransitionViewModel, systemPropertiesHelper, wmLockscreenVisibilityManager, - selectedUserInteractor); + selectedUserInteractor, + keyguardInteractor); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index eceaf6c3c4fd..4d60dd0bea62 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -187,7 +187,8 @@ constructor( faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false private val _isAuthRunning = MutableStateFlow(false) - override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning + override val isAuthRunning: StateFlow<Boolean> + get() = _isAuthRunning private val keyguardSessionId: InstanceId? get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD) @@ -253,13 +254,6 @@ constructor( ) .andAllFlows("canFaceAuthRun", faceAuthLog) .flowOn(backgroundDispatcher) - .onEach { - faceAuthLogger.canFaceAuthRunChanged(it) - if (!it) { - // Cancel currently running auth if any of the gating checks are false. - cancel() - } - } .stateIn(applicationScope, SharingStarted.Eagerly, false) // Face detection can run only when lockscreen bypass is enabled @@ -287,12 +281,9 @@ constructor( ) .andAllFlows("canFaceDetectRun", faceDetectLog) .flowOn(backgroundDispatcher) - .onEach { - if (!it) { - cancelDetection() - } - } .stateIn(applicationScope, SharingStarted.Eagerly, false) + observeFaceAuthGatingChecks() + observeFaceDetectGatingChecks() observeFaceAuthResettingConditions() listenForSchedulingWatchdog() processPendingAuthRequests() @@ -313,14 +304,14 @@ constructor( } private fun observeFaceAuthResettingConditions() { - // Clear auth status when keyguard is going away or when the user is switching or device - // starts going to sleep. + // Clear auth status when keyguard done animations finished or when the user is switching + // or device starts going to sleep. merge( powerInteractor.isAsleep, if (featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { keyguardTransitionInteractor.isInTransitionToState(KeyguardState.GONE) } else { - keyguardRepository.isKeyguardGoingAway + keyguardRepository.keyguardDoneAnimationsFinished.map { true } }, userRepository.selectedUser.map { it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS @@ -347,6 +338,17 @@ constructor( pendingAuthenticateRequest.value = null } + private fun observeFaceDetectGatingChecks() { + canRunDetection + .onEach { + if (!it) { + cancelDetection() + } + } + .flowOn(mainDispatcher) + .launchIn(applicationScope) + } + private fun isUdfps() = deviceEntryFingerprintAuthRepository.availableFpSensorType.map { it == BiometricType.UNDER_DISPLAY_FINGERPRINT @@ -405,6 +407,20 @@ constructor( ) } + private fun observeFaceAuthGatingChecks() { + canRunFaceAuth + .onEach { + faceAuthLogger.canFaceAuthRunChanged(it) + if (!it) { + // Cancel currently running auth if any of the gating checks are false. + faceAuthLogger.cancellingFaceAuth() + cancel() + } + } + .flowOn(mainDispatcher) + .launchIn(applicationScope) + } + private val faceAuthCallback = object : FaceManager.AuthenticationCallback() { override fun onAuthenticationFailed() { @@ -539,7 +555,7 @@ constructor( authenticate(it.uiEvent, it.fallbackToDetection) } } - .flowOn(backgroundDispatcher) + .flowOn(mainDispatcher) .launchIn(applicationScope) } @@ -635,7 +651,6 @@ constructor( override fun cancel() { if (authCancellationSignal == null) return - faceAuthLogger.cancellingFaceAuth() authCancellationSignal?.cancel() cancelNotReceivedHandlerJob?.cancel() cancelNotReceivedHandlerJob = 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/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 0df7f9b809fd..31ef100abbcb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -192,6 +192,17 @@ interface KeyguardRepository { /** Observable updated when keyguardDone should be called either now or soon. */ val keyguardDone: Flow<KeyguardDone> + /** + * Emits after the keyguard is done animating away. + * + * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed. + */ + @Deprecated( + "Use KeyguardTransitionInteractor flows instead. The closest match for " + + "'keyguardDoneAnimationsFinished' is when the GONE transition is finished." + ) + val keyguardDoneAnimationsFinished: Flow<Unit> + /** Receive whether clock should be centered on lockscreen. */ val clockShouldBeCentered: Flow<Boolean> @@ -239,6 +250,17 @@ interface KeyguardRepository { suspend fun setKeyguardDone(keyguardDoneType: KeyguardDone) fun setClockShouldBeCentered(shouldBeCentered: Boolean) + + /** + * Updates signal that the keyguard done animations are finished + * + * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed. + */ + @Deprecated( + "Use KeyguardTransitionInteractor flows instead. The closest match for " + + "'keyguardDoneAnimationsFinished' is when the GONE transition is finished." + ) + fun keyguardDoneAnimationsFinished() } /** Encapsulates application state for the keyguard. */ @@ -269,6 +291,11 @@ constructor( _keyguardDone.emit(keyguardDoneType) } + override val keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> = MutableSharedFlow() + override fun keyguardDoneAnimationsFinished() { + keyguardDoneAnimationsFinished.tryEmit(Unit) + } + private val _animateBottomAreaDozingTransitions = MutableStateFlow(false) override val animateBottomAreaDozingTransitions = _animateBottomAreaDozingTransitions.asStateFlow() 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/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index defca18b64b8..21651ba2cc2b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -318,6 +318,10 @@ constructor( repository.ambientIndicationVisible.value = isVisible } + fun keyguardDoneAnimationsFinished() { + repository.keyguardDoneAnimationsFinished() + } + companion object { private const val TAG = "KeyguardInteractor" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt index fb20000471a2..e3f47397eca3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt @@ -31,7 +31,6 @@ 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.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository @@ -72,7 +71,6 @@ constructor( private val context: Context, @Application private val applicationScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, - @Background private val backgroundDispatcher: CoroutineDispatcher, private val repository: DeviceEntryFaceAuthRepository, private val primaryBouncerInteractor: Lazy<PrimaryBouncerInteractor>, private val alternateBouncerInteractor: AlternateBouncerInteractor, @@ -109,7 +107,6 @@ constructor( fallbackToDetect = false ) } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) alternateBouncerInteractor.isVisible @@ -121,7 +118,6 @@ constructor( fallbackToDetect = false ) } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) merge( @@ -150,7 +146,6 @@ constructor( fallbackToDetect = true ) } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) deviceEntryFingerprintAuthRepository.isLockedOut @@ -163,7 +158,6 @@ constructor( } } } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) // User switching should stop face auth and then when it is complete we should trigger face @@ -187,7 +181,6 @@ constructor( ) } } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) } @@ -302,7 +295,6 @@ constructor( trustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, userInfo.id) } } - .flowOn(backgroundDispatcher) .onEach { (isAuthenticated, _) -> listeners.forEach { it.onAuthenticatedChanged(isAuthenticated) } } 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/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt index acfd3b0bcf57..24240dfe7402 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt @@ -30,6 +30,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants +import com.android.systemui.util.kotlin.logD import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -58,11 +59,14 @@ constructor( observer = PreviewLifecycleObserver( - renderer, applicationScope, mainDispatcher, + renderer, ::destroyObserver, ) + + logD(TAG) { "Created observer $observer" } + // Destroy any previous renderer associated with this token. activePreviews[renderer.id]?.let { destroyObserver(it) } activePreviews[renderer.id] = observer @@ -80,6 +84,8 @@ constructor( observer, ) ) + // NOTE: The process on the other side can retain messenger indefinitely. + // (e.g. GC might not trigger and cleanup the reference) val msg = Message.obtain() msg.replyTo = messenger result.putParcelable(KEY_PREVIEW_CALLBACK, msg) @@ -99,57 +105,84 @@ constructor( } } - private class PreviewLifecycleObserver( - private val renderer: KeyguardPreviewRenderer, - private val scope: CoroutineScope, - private val mainDispatcher: CoroutineDispatcher, - private val requestDestruction: (PreviewLifecycleObserver) -> Unit, - ) : Handler.Callback, IBinder.DeathRecipient { + companion object { + internal const val TAG = "KeyguardRemotePreviewManager" + @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package" + @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback" + } +} - private var isDestroyedOrDestroying = false +/** + * Handles messages from the other process and handles cleanup. + * + * NOTE: The other process might hold on to reference of this class indefinitely. It's entirely + * possible that GC won't trigger and we'll leak this for all times even if [onDestroy] was called. + * This helps make sure no non-Singleton objects are retained beyond destruction to prevent leaks. + */ +@VisibleForTesting(VisibleForTesting.PRIVATE) +class PreviewLifecycleObserver( + private val scope: CoroutineScope, + private val mainDispatcher: CoroutineDispatcher, + renderer: KeyguardPreviewRenderer, + onDestroy: (PreviewLifecycleObserver) -> Unit, +) : Handler.Callback, IBinder.DeathRecipient { + + private var isDestroyedOrDestroying = false + // These two are null after destruction + @VisibleForTesting var renderer: KeyguardPreviewRenderer? + @VisibleForTesting var onDestroy: ((PreviewLifecycleObserver) -> Unit)? + + init { + this.renderer = renderer + this.onDestroy = onDestroy + } - override fun handleMessage(message: Message): Boolean { - if (isDestroyedOrDestroying) { - return true - } + override fun handleMessage(message: Message): Boolean { + if (isDestroyedOrDestroying) { + return true + } - when (message.what) { - KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> { - message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId -> - renderer.onSlotSelected(slotId = slotId) - } + when (message.what) { + KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> { + message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId -> + checkNotNull(renderer).onSlotSelected(slotId = slotId) } - KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> { - renderer.hideSmartspace( + } + KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> { + checkNotNull(renderer) + .hideSmartspace( message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE) ) - } - else -> requestDestruction(this) } - - return true + else -> checkNotNull(onDestroy).invoke(this) } - override fun binderDied() { - requestDestruction(this) + return true + } + + override fun binderDied() { + onDestroy?.invoke(this) + } + + fun onDestroy(): Pair<IBinder?, Int>? { + if (isDestroyedOrDestroying) { + return null } - fun onDestroy(): Pair<IBinder?, Int>? { - if (isDestroyedOrDestroying) { - return null - } + logD(TAG) { "Destroying $this" } - isDestroyedOrDestroying = true - val hostToken = renderer.hostToken + isDestroyedOrDestroying = true + return renderer?.let { rendererToDestroy -> + this.renderer = null + this.onDestroy = null + val hostToken = rendererToDestroy.hostToken hostToken?.unlinkToDeath(this, 0) - scope.launch(mainDispatcher) { renderer.destroy() } - return renderer.id + scope.launch(mainDispatcher) { rendererToDestroy.destroy() } + rendererToDestroy.id } } companion object { private const val TAG = "KeyguardRemotePreviewManager" - @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package" - @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback" } } 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/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt index 14d4b6800b0a..c87fd14a943d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt @@ -119,7 +119,7 @@ internal constructor( ::AnimatingColorTransition ) - val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_secondary95) + val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20) val surfaceColor = animatingColorTransitionFactory(bgColor, ::surfaceFromScheme) { surfaceColor -> val colorList = ColorStateList.valueOf(surfaceColor) 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 a4088f81f062..66da8bdd0311 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles +import android.app.AlertDialog import android.content.Intent import android.os.Handler import android.os.Looper @@ -24,8 +25,11 @@ import android.text.TextUtils import android.view.View import android.widget.Switch import androidx.annotation.VisibleForTesting +import com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN import com.android.internal.logging.MetricsLogger import com.android.systemui.Flags.recordIssueQsTile +import com.android.systemui.animation.DialogCuj +import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter @@ -36,7 +40,12 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger 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 import javax.inject.Inject class RecordIssueTile @@ -50,7 +59,12 @@ constructor( metricsLogger: MetricsLogger, statusBarStateController: StatusBarStateController, activityStarter: ActivityStarter, - qsLogger: QSLogger + qsLogger: QSLogger, + private val keyguardDismissUtil: KeyguardDismissUtil, + private val keyguardStateController: KeyguardStateController, + private val dialogLaunchAnimator: DialogLaunchAnimator, + private val sysuiDialogFactory: SystemUIDialog.Factory, + private val userContextProvider: UserContextProvider, ) : QSTileImpl<QSTile.BooleanState>( host, @@ -76,11 +90,41 @@ constructor( handlesLongClick = false } - override fun handleClick(view: View?) { - isRecording = !isRecording + @VisibleForTesting + public override fun handleClick(view: View?) { + if (isRecording) { + isRecording = false + } else { + mUiHandler.post { showPrompt(view) } + } refreshState() } + private fun showPrompt(view: View?) { + val dialog: AlertDialog = + RecordIssueDialogDelegate(sysuiDialogFactory, userContextProvider) { + isRecording = true + refreshState() + } + .createDialog() + val dismissAction = + ActivityStarter.OnDismissAction { + // We animate from the touched view only if we are not on the keyguard, given + // that if we are we will dismiss it which will also collapse the shade. + if (view != null && !keyguardStateController.isShowing) { + dialogLaunchAnimator.showFromView( + dialog, + view, + DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC) + ) + } else { + dialog.show() + } + false + } + keyguardDismissUtil.executeWhenUnlocked(dismissAction, false, true) + } + override fun getLongClickIntent(): Intent? = null @VisibleForTesting 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 new file mode 100644 index 000000000000..5bf44fa5b143 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt @@ -0,0 +1,113 @@ +/* + * 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.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 +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.WindowManager +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 { + + @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch + private lateinit var issueTypeButton: Button + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + dialog.apply { + setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null)) + setTitle(context.getString(R.string.qs_record_issue_label)) + setIcon(R.drawable.qs_record_issue_icon_off) + setNegativeButton(R.string.cancel) { _, _ -> dismiss() } + setPositiveButton(R.string.qs_record_issue_start) { _, _ -> + onStarted.run() + if (screenRecordSwitch.isChecked) { + requestScreenCapture() + } + dismiss() + } + } + } + + override fun createDialog(): SystemUIDialog = factory.create(this) + + override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + dialog.apply { + window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) + window?.setGravity(Gravity.CENTER) + + screenRecordSwitch = requireViewById(R.id.screenrecord_switch) + issueTypeButton = requireViewById(R.id.issue_type_button) + issueTypeButton.setOnClickListener { onIssueTypeClicked(context) } + } + } + + private fun onIssueTypeClicked(context: Context) { + val selectedCategory = issueTypeButton.text.toString() + val popupMenu = PopupMenu(context, issueTypeButton) + + context.resources.getStringArray(R.array.qs_record_issue_types).forEachIndexed { i, cat -> + popupMenu.menu.add(0, 0, i, cat).apply { + setIcon(R.drawable.arrow_pointing_down) + if (selectedCategory != cat) { + iconTintList = ColorStateList.valueOf(Color.TRANSPARENT) + } + } + } + popupMenu.apply { + setOnMenuItemClickListener { + issueTypeButton.text = it.title + true + } + setForceShowIcon(true) + 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/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt index c59ef2632f15..d26fded19cc1 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt @@ -59,6 +59,11 @@ abstract class SmartspaceModule { * The BcSmartspaceDataPlugin for the standalone weather. */ const val WEATHER_SMARTSPACE_DATA_PLUGIN = "weather_smartspace_data_plugin" + + /** + * The BcSmartspaceDataProvider for the glanceable hub. + */ + const val GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN = "glanceable_hub_smartspace_data_plugin" } @BindsOptionalOf @@ -78,4 +83,8 @@ abstract class SmartspaceModule { abstract fun bindSmartspacePrecondition( lockscreenPrecondition: LockscreenPrecondition? ): SmartspacePrecondition? + + @BindsOptionalOf + @Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN) + abstract fun optionalBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin? } diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt index 2fc0ec290a90..095d30ef55df 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt @@ -19,9 +19,9 @@ package com.android.systemui.smartspace.data.repository import android.app.smartspace.SmartspaceTarget import android.os.Parcelable import android.widget.RemoteViews +import com.android.systemui.communal.smartspace.CommunalSmartspaceController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.BcSmartspaceDataPlugin -import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -32,37 +32,37 @@ interface SmartspaceRepository { /** Whether [RemoteViews] are passed through smartspace targets. */ val isSmartspaceRemoteViewsEnabled: Boolean - /** Smartspace targets for the lockscreen surface. */ - val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> + /** Smartspace targets for the communal surface. */ + val communalSmartspaceTargets: Flow<List<SmartspaceTarget>> } @SysUISingleton class SmartspaceRepositoryImpl @Inject constructor( - private val lockscreenSmartspaceController: LockscreenSmartspaceController, + private val communalSmartspaceController: CommunalSmartspaceController, ) : SmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener { override val isSmartspaceRemoteViewsEnabled: Boolean get() = android.app.smartspace.flags.Flags.remoteViews() - private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> = + private val _communalSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> = MutableStateFlow(emptyList()) - override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> = - _lockscreenSmartspaceTargets + override val communalSmartspaceTargets: Flow<List<SmartspaceTarget>> = + _communalSmartspaceTargets .onStart { - lockscreenSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl) + communalSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl) } .onCompletion { - lockscreenSmartspaceController.removeListener( + communalSmartspaceController.removeListener( listener = this@SmartspaceRepositoryImpl ) } override fun onSmartspaceTargetsUpdated(targetsNullable: MutableList<out Parcelable>?) { targetsNullable?.let { targets -> - _lockscreenSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>() + _communalSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>() } - ?: run { _lockscreenSmartspaceTargets.value = emptyList() } + ?: run { _communalSmartspaceTargets.value = emptyList() } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index 92391e7c76f0..e1e30e1d74f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder import android.graphics.Color import android.graphics.Rect +import android.util.Log import android.view.View +import android.view.ViewGroup import android.widget.FrameLayout import androidx.annotation.ColorInt import androidx.collection.ArrayMap @@ -220,7 +222,7 @@ object NotificationIconContainerViewBinder { notifyBindingFailures: (Collection<String>) -> Unit, viewStore: IconViewStore, bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> }, - ): Unit = coroutineScope { + ) { val iconSizeFlow: Flow<Int> = configuration.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size_sp, @@ -235,6 +237,21 @@ object NotificationIconContainerViewBinder { -> FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight) } + try { + bindIcons(view, layoutParams, notifyBindingFailures, viewStore, bindIcon) + } finally { + // Detach everything so that child SBIVs don't hold onto a reference to the container. + view.detachAllIcons() + } + } + + private suspend fun Flow<NotificationIconsViewData>.bindIcons( + view: NotificationIconContainer, + layoutParams: Flow<FrameLayout.LayoutParams>, + notifyBindingFailures: (Collection<String>) -> Unit, + viewStore: IconViewStore, + bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit, + ): Unit = coroutineScope { val failedBindings = mutableSetOf<String>() val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>() var prevIcons = NotificationIconsViewData() @@ -266,9 +283,17 @@ object NotificationIconContainerViewBinder { continue } failedBindings.remove(notifKey) - // The view might still be transiently added if it was just removed and added - // again - view.removeTransientView(sbiv) + (sbiv.parent as? ViewGroup)?.run { + if (this !== view) { + Log.wtf(TAG, "StatusBarIconView($notifKey) has an unexpected parent") + } + // If the container was re-inflated and re-bound, then SBIVs might still be + // attached to the prior view. + removeView(sbiv) + // The view might still be transiently added if it was just removed and + // added again. + removeTransientView(sbiv) + } view.addView(sbiv, idx) boundViewsByNotifKey.remove(notifKey)?.second?.cancel() boundViewsByNotifKey[notifKey] = @@ -351,7 +376,8 @@ object NotificationIconContainerViewBinder { fun iconView(key: String): StatusBarIconView? } - @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE + @ColorInt private const val DEFAULT_AOD_ICON_COLOR = Color.WHITE + private const val TAG = "NotifIconContainerViewBinder" } /** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 00e78a49ba19..0dabafbdecb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -400,6 +400,21 @@ public class NotificationIconContainer extends ViewGroup { } } + /** + * Removes all child {@link StatusBarIconView} instances from this container, immediately and + * without animation. This should be called when tearing down this container so that external + * icon views are not holding onto a reference thru {@link View#getParent()}. + */ + public void detachAllIcons() { + boolean animsWereEnabled = mAnimationsEnabled; + boolean wasChangingPositions = mChangingViewPositions; + mAnimationsEnabled = false; + mChangingViewPositions = true; + removeAllViews(); + mChangingViewPositions = wasChangingPositions; + mAnimationsEnabled = animsWereEnabled; + } + public boolean areIconsOverflowing() { return mIsShowingOverflowDot; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index 756c440eca89..0c5472f0ecfb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -41,6 +41,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.log.core.LogLevel; import com.android.systemui.res.R; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; @@ -49,6 +50,7 @@ import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Objects; +import java.util.function.Consumer; import javax.inject.Inject; @@ -210,23 +212,33 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private void notifyKeyguardChanged() { Trace.beginSection("KeyguardStateController#notifyKeyguardChanged"); // Copy the list to allow removal during callback. - new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged); + invokeForEachCallback(Callback::onKeyguardShowingChanged); Trace.endSection(); } private void notifyKeyguardFaceAuthEnabledChanged() { + invokeForEachCallback(Callback::onFaceEnrolledChanged); + } + + private void invokeForEachCallback(Consumer<Callback> consumer) { // Copy the list to allow removal during callback. - new ArrayList<>(mCallbacks).forEach(callback -> { + ArrayList<Callback> copyOfCallbacks = new ArrayList<>(mCallbacks); + for (int i = 0; i < copyOfCallbacks.size(); i++) { + Callback callback = copyOfCallbacks.get(i); + // Temporary fix for b/315731775, callback is null even though only non-null callbacks + // are added to the list by addCallback if (callback != null) { - callback.onFaceEnrolledChanged(); + consumer.accept(callback); + } else { + mLogger.log("KeyguardStateController callback is null", LogLevel.DEBUG); } - }); + } } private void notifyUnlockedChanged() { Trace.beginSection("KeyguardStateController#notifyUnlockedChanged"); // Copy the list to allow removal during callback. - new ArrayList<>(mCallbacks).forEach(Callback::onUnlockedChanged); + invokeForEachCallback(Callback::onUnlockedChanged); Trace.endSection(); } @@ -242,10 +254,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardFadingAway", keyguardFadingAway ? 1 : 0); mKeyguardFadingAway = keyguardFadingAway; - ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks); - for (int i = 0; i < callbacks.size(); i++) { - callbacks.get(i).onKeyguardFadingAwayChanged(); - } + invokeForEachCallback(Callback::onKeyguardFadingAwayChanged); } } @@ -359,7 +368,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardGoingAway", keyguardGoingAway ? 1 : 0); mKeyguardGoingAway = keyguardGoingAway; - new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardGoingAwayChanged); + invokeForEachCallback(Callback::onKeyguardGoingAwayChanged); } } @@ -368,7 +377,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum if (mPrimaryBouncerShowing != showing) { mPrimaryBouncerShowing = showing; - new ArrayList<>(mCallbacks).forEach(Callback::onPrimaryBouncerShowingChanged); + invokeForEachCallback(Callback::onPrimaryBouncerShowingChanged); } } @@ -392,13 +401,13 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum boolean dismissingFromTouch) { mDismissAmount = dismissAmount; mDismissingFromTouch = dismissingFromTouch; - new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardDismissAmountChanged); + invokeForEachCallback(Callback::onKeyguardDismissAmountChanged); } @Override public void setLaunchTransitionFadingAway(boolean fadingAway) { mLaunchTransitionFadingAway = fadingAway; - new ArrayList<>(mCallbacks).forEach(Callback::onLaunchTransitionFadingAwayChanged); + invokeForEachCallback(Callback::onLaunchTransitionFadingAwayChanged); } @Override 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/src/com/android/systemui/util/kotlin/Log.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Log.kt new file mode 100644 index 000000000000..2f6c450e924c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Log.kt @@ -0,0 +1,40 @@ +/* + * 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.util.kotlin + +import android.util.Log + +/** Logs message at [Log.DEBUG] level. Won't call the lambda if [DEBUG] is not loggable. */ +inline fun logD(tag: String, messageLambda: () -> String) { + if (Log.isLoggable(tag, Log.DEBUG)) { + Log.d(tag, messageLambda.invoke()) + } +} + +/** Logs message at [Log.VERBOSE] level. Won't call the lambda if [VERBOSE] is not loggable. */ +inline fun logV(tag: String, messageLambda: () -> String) { + if (Log.isLoggable(tag, Log.VERBOSE)) { + Log.v(tag, messageLambda.invoke()) + } +} + +/** Logs message at [Log.INFO] level. Won't call the lambda if [INFO] is not loggable. */ +inline fun logI(tag: String, messageLambda: () -> String) { + if (Log.isLoggable(tag, Log.INFO)) { + Log.i(tag, messageLambda.invoke()) + } +} 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/contrast/ContrastDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt index e931384fd61e..65f68f9df3e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt @@ -22,6 +22,7 @@ import android.provider.Settings import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater +import android.view.View import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -39,6 +40,8 @@ import com.android.systemui.util.time.FakeSystemClock import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.eq import org.mockito.Mockito.verify @@ -73,13 +76,20 @@ class ContrastDialogDelegateTest : SysuiTestCase() { if (Looper.myLooper() == null) Looper.prepare() mContrastDialogDelegate = - ContrastDialogDelegate( - sysuiDialogFactory, - mainExecutor, - mockUiModeManager, - mockUserTracker, - mockSecureSettings - ) + ContrastDialogDelegate( + sysuiDialogFactory, + mainExecutor, + mockUiModeManager, + mockUserTracker, + mockSecureSettings + ) + + mContrastDialogDelegate.createDialog() + val viewCaptor = ArgumentCaptor.forClass(View::class.java) + verify(sysuiDialog).setView(viewCaptor.capture()) + whenever(sysuiDialog.requireViewById(anyInt()) as View?).then { + viewCaptor.value.requireViewById(it.getArgument(0)) + } } @Test 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 ae5f625b1c8d..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; @@ -93,6 +94,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.flags.SystemPropertiesHelper; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.log.SessionTracker; import com.android.systemui.navigationbar.NavigationModeController; @@ -190,6 +192,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ShadeInteractor mShadeInteractor; private @Mock ShadeWindowLogger mShadeWindowLogger; private @Mock SelectedUserInteractor mSelectedUserInteractor; + private @Mock KeyguardInteractor mKeyguardInteractor; private @Captor ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback; private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback> @@ -264,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); @@ -1131,7 +1134,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { () -> mDreamingToLockscreenTransitionViewModel, mSystemPropertiesHelper, () -> mock(WindowManagerLockscreenVisibilityManager.class), - mSelectedUserInteractor); + mSelectedUserInteractor, + mKeyguardInteractor); mViewMediator.start(); mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null); 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/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt index 4ab8e28bc232..6eb95bddaf53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -137,7 +137,6 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { mContext, testScope.backgroundScope, dispatcher, - dispatcher, faceAuthRepository, { PrimaryBouncerInteractor( 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 d8199c527cac..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 @@ -23,6 +23,7 @@ import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -30,12 +31,20 @@ 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 +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.ArgumentMatchers.isA import org.mockito.Mock +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations /** @@ -53,15 +62,23 @@ class RecordIssueTileTest : SysuiTestCase() { @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var qsLogger: QSLogger - + @Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil + @Mock private lateinit var keyguardStateController: KeyguardStateController + @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 @Before fun setUp() { MockitoAnnotations.initMocks(this) whenever(host.context).thenReturn(mContext) + whenever(dialogFactory.create(any())).thenReturn(dialog) - val testableLooper = TestableLooper.get(this) + testableLooper = TestableLooper.get(this) tile = RecordIssueTile( host, @@ -72,7 +89,12 @@ class RecordIssueTileTest : SysuiTestCase() { metricsLogger, statusBarStateController, activityStarter, - qsLogger + qsLogger, + keyguardDismissUtil, + keyguardStateController, + dialogLauncherAnimator, + dialogFactory, + userContextProvider, ) } @@ -119,4 +141,18 @@ class RecordIssueTileTest : SysuiTestCase() { assertThat(testState.state).isEqualTo(Tile.STATE_ACTIVE) } + + @Test + fun showPrompt_shouldUseKeyguardDismissUtil_ToShowDialog() { + tile.isRecording = false + tile.handleClick(null) + testableLooper.processAllMessages() + + verify(keyguardDismissUtil) + .executeWhenUnlocked( + isA(ActivityStarter.OnDismissAction::class.java), + eq(false), + eq(true) + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt new file mode 100644 index 000000000000..c5d35245ba7c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt @@ -0,0 +1,94 @@ +/* + * 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.recordissue + +import android.app.Dialog +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.widget.Button +import android.widget.Switch +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.model.SysUiState +import com.android.systemui.res.R +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.SystemUIDialogManager +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class RecordIssueDialogDelegateTest : SysuiTestCase() { + + private lateinit var dialog: SystemUIDialog + private lateinit var latch: CountDownLatch + + @Before + fun setup() { + val dialogFactory = + SystemUIDialog.Factory( + context, + mock<FeatureFlags>(), + mock<SystemUIDialogManager>(), + mock<SysUiState>().apply { + whenever(setFlag(anyInt(), anyBoolean())).thenReturn(this) + }, + mock<BroadcastDispatcher>(), + mock<DialogLaunchAnimator>() + ) + + latch = CountDownLatch(1) + dialog = + RecordIssueDialogDelegate(dialogFactory, mock()) { latch.countDown() }.createDialog() + dialog.show() + } + + @After + fun teardown() { + dialog.dismiss() + } + + @Test + fun dialog_hasCorrectUiElements_afterCreation() { + dialog.requireViewById<Switch>(R.id.screenrecord_switch) + dialog.requireViewById<Button>(R.id.issue_type_button) + + assertThat(dialog.getButton(Dialog.BUTTON_POSITIVE).text) + .isEqualTo(context.getString(R.string.qs_record_issue_start)) + assertThat(dialog.getButton(Dialog.BUTTON_NEGATIVE).text) + .isEqualTo(context.getString(R.string.cancel)) + } + + @Test + fun onStarted_isCalled_afterStartButtonIsClicked() { + dialog.getButton(Dialog.BUTTON_POSITIVE).callOnClick() + latch.await(1L, TimeUnit.MILLISECONDS) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java index 0f33aaf20195..2c7b60660165 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java @@ -56,6 +56,7 @@ import com.google.common.util.concurrent.Futures; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -138,6 +139,7 @@ public final class AppClipsActivityTest extends SysuiTestCase { } @Test + @Ignore("b/315848285") public void screenshotDisplayed_userConsented_screenshotExportedSuccessfully() { ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> { assertThat(resultCode).isEqualTo(RESULT_OK); 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/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index c5d745a65e96..4200f05ad64b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -41,6 +41,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { private val _deferKeyguardDone: MutableSharedFlow<KeyguardDone> = MutableSharedFlow() override val keyguardDone: Flow<KeyguardDone> = _deferKeyguardDone + private val _keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> = + MutableSharedFlow(extraBufferCapacity = 1) + override val keyguardDoneAnimationsFinished: Flow<Unit> = _keyguardDoneAnimationsFinished + private val _clockShouldBeCentered = MutableStateFlow<Boolean>(true) override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered @@ -176,6 +180,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { _deferKeyguardDone.emit(timing) } + override fun keyguardDoneAnimationsFinished() { + _keyguardDoneAnimationsFinished.tryEmit(Unit) + } + override fun setClockShouldBeCentered(shouldBeCentered: Boolean) { _clockShouldBeCentered.value = shouldBeCentered } 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/smartspace/data/repository/FakeSmartspaceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt index c8013ef96fa7..862e52d7703f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt @@ -10,12 +10,12 @@ class FakeSmartspaceRepository( override val isSmartspaceRemoteViewsEnabled = smartspaceRemoteViewsEnabled - private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> = + private val _communalSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> = MutableStateFlow(emptyList()) - override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> = - _lockscreenSmartspaceTargets + override val communalSmartspaceTargets: Flow<List<SmartspaceTarget>> = + _communalSmartspaceTargets - fun setLockscreenSmartspaceTargets(targets: List<SmartspaceTarget>) { - _lockscreenSmartspaceTargets.value = targets + fun setCommunalSmartspaceTargets(targets: List<SmartspaceTarget>) { + _communalSmartspaceTargets.value = targets } } 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/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index b315f4a0f0c5..7fcef9c90812 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -76,6 +76,7 @@ import android.util.Size; import android.view.Surface; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl; import androidx.camera.extensions.impl.AutoPreviewExtenderImpl; import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl; @@ -94,6 +95,7 @@ import androidx.camera.extensions.impl.PreviewExtenderImpl; import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType; import androidx.camera.extensions.impl.PreviewImageProcessorImpl; import androidx.camera.extensions.impl.ProcessResultImpl; +import androidx.camera.extensions.impl.ProcessorImpl; import androidx.camera.extensions.impl.RequestUpdateProcessorImpl; import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl; @@ -101,6 +103,7 @@ import androidx.camera.extensions.impl.advanced.BeautyAdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl; import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl; +import androidx.camera.extensions.impl.advanced.EyesFreeVideographyAdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.ImageProcessorImpl; import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl; @@ -112,6 +115,8 @@ import androidx.camera.extensions.impl.advanced.RequestProcessorImpl; import androidx.camera.extensions.impl.advanced.SessionProcessorImpl; import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl; +import com.android.internal.camera.flags.Flags; + import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -135,22 +140,28 @@ public class CameraExtensionsProxyService extends Service { private static final String RESULTS_VERSION_PREFIX = "1.3"; // Support for various latency improvements private static final String LATENCY_VERSION_PREFIX = "1.4"; - private static final String[] ADVANCED_VERSION_PREFIXES = {LATENCY_VERSION_PREFIX, - ADVANCED_VERSION_PREFIX, RESULTS_VERSION_PREFIX }; - private static final String[] SUPPORTED_VERSION_PREFIXES = {LATENCY_VERSION_PREFIX, - RESULTS_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, "1.1", NON_INIT_VERSION_PREFIX}; + private static final String EFV_VERSION_PREFIX = "1.5"; + private static final String[] ADVANCED_VERSION_PREFIXES = {EFV_VERSION_PREFIX, + LATENCY_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, RESULTS_VERSION_PREFIX }; + private static final String[] SUPPORTED_VERSION_PREFIXES = {EFV_VERSION_PREFIX, + LATENCY_VERSION_PREFIX, RESULTS_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, "1.1", + NON_INIT_VERSION_PREFIX}; private static final boolean EXTENSIONS_PRESENT = checkForExtensions(); private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ? (new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null; private static final boolean ESTIMATED_LATENCY_API_SUPPORTED = checkForLatencyAPI(); private static final boolean LATENCY_IMPROVEMENTS_SUPPORTED = EXTENSIONS_PRESENT && - (EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX)); + (EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX) || + (EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX))); + private static final boolean EFV_SUPPORTED = EXTENSIONS_PRESENT && + (EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX)); private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI(); private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT && (!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX)); private static final boolean RESULT_API_SUPPORTED = EXTENSIONS_PRESENT && (EXTENSIONS_VERSION.startsWith(RESULTS_VERSION_PREFIX) || - EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX)); + EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX) || + EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX)); private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>(); private CameraManager mCameraManager; @@ -509,6 +520,167 @@ public class CameraExtensionsProxyService extends Service { */ public static Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> initializeExtension( int extensionType) { + if (Flags.concertMode()) { + if (extensionType == CameraExtensionCharacteristics.EXTENSION_EYES_FREE_VIDEOGRAPHY) { + // Basic extensions are deprecated starting with extension version 1.5 + return new Pair<>(new PreviewExtenderImpl() { + @Override + public boolean isExtensionAvailable(String cameraId, + CameraCharacteristics cameraCharacteristics) { + return false; + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl getCaptureStage() { + return null; + } + + @Override + public ProcessorType getProcessorType() { + return null; + } + + @Override + public ProcessorImpl getProcessor() { + return null; + } + + @Nullable + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + return null; + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { } + + @Override + public void onDeInit() { } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onPresetSession() { + return null; + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onEnableSession() { + return null; + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onDisableSession() { + return null; + } + + @Override + public int onSessionType() { + return 0; + } + }, new ImageCaptureExtenderImpl() { + @Override + public boolean isExtensionAvailable(String cameraId, + CameraCharacteristics cameraCharacteristics) { + return false; + } + + @Override + public void init(String cameraId, + CameraCharacteristics cameraCharacteristics) { } + + @Override + public CaptureProcessorImpl getCaptureProcessor() { + return null; + } + + @Override + public + List<androidx.camera.extensions.impl.CaptureStageImpl> getCaptureStages() { + return null; + } + + @Override + public int getMaxCaptureStage() { + return 0; + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + return null; + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions( + Size captureSize) { + return null; + } + + @Override + public Range<Long> getEstimatedCaptureLatencyRange( + Size captureOutputSize) { + return null; + } + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + return null; + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + return null; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return false; + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + return null; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } + + @Override + public void onInit(String cameraId, + CameraCharacteristics cameraCharacteristics, Context context) { } + + @Override + public void onDeInit() { } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onPresetSession() { + return null; + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onEnableSession() { + return null; + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onDisableSession() { + return null; + } + + @Override + public int onSessionType() { + return 0; + } + }); + } + } + switch (extensionType) { case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC: return new Pair<>(new AutoPreviewExtenderImpl(), @@ -533,6 +705,82 @@ public class CameraExtensionsProxyService extends Service { * @hide */ public static AdvancedExtenderImpl initializeAdvancedExtensionImpl(int extensionType) { + if (Flags.concertMode()) { + if (extensionType == CameraExtensionCharacteristics.EXTENSION_EYES_FREE_VIDEOGRAPHY) { + if (EFV_SUPPORTED) { + return new EyesFreeVideographyAdvancedExtenderImpl(); + } else { + return new AdvancedExtenderImpl() { + @Override + public boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + return false; + } + + @Override + public void init(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + + } + + @Override + public Range<Long> getEstimatedCaptureLatencyRange(String cameraId, + Size captureOutputSize, int imageFormat) { + return null; + } + + @Override + public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions( + String cameraId) { + return null; + } + + @Override + public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions( + String cameraId) { + return null; + } + + @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions( + Size captureSize) { + return null; + } + + @Override + public List<Size> getSupportedYuvAnalysisResolutions(String cameraId) { + return null; + } + + @Override + public SessionProcessorImpl createSessionProcessor() { + return null; + } + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + return null; + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + return null; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return false; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } + }; + } + } + } + switch (extensionType) { case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC: return new AutoAdvancedExtenderImpl(); diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index d175713eb92f..513c09587026 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -16,6 +16,8 @@ package android.platform.test.ravenwood; +import static org.junit.Assert.fail; + import android.platform.test.annotations.IgnoreUnderRavenwood; import org.junit.Assume; @@ -36,6 +38,15 @@ public class RavenwoodRule implements TestRule { private static final boolean IS_UNDER_RAVENWOOD = RavenwoodRuleImpl.isUnderRavenwood(); + /** + * When probing is enabled, all tests will be unconditionally run under Ravenwood to detect + * cases where a test is able to pass despite being marked as {@code IgnoreUnderRavenwood}. + * + * This is typically helpful for internal maintainers discovering tests that had previously + * been ignored, but now have enough Ravenwood-supported functionality to be enabled. + */ + private static final boolean ENABLE_PROBE_IGNORED = false; // DO NOT SUBMIT WITH TRUE + private static final int SYSTEM_UID = 1000; private static final int NOBODY_UID = 9999; private static final int FIRST_APPLICATION_UID = 10000; @@ -97,26 +108,76 @@ public class RavenwoodRule implements TestRule { return IS_UNDER_RAVENWOOD; } + /** + * Test if the given {@link Description} has been marked with an {@link IgnoreUnderRavenwood} + * annotation, either at the method or class level. + */ + private static boolean hasIgnoreUnderRavenwoodAnnotation(Description description) { + if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) { + return true; + } else if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { + return true; + } else { + return false; + } + } + @Override public Statement apply(Statement base, Description description) { + if (ENABLE_PROBE_IGNORED) { + return applyProbeIgnored(base, description); + } else { + return applyDefault(base, description); + } + } + + /** + * Run the given {@link Statement} with no special treatment. + */ + private Statement applyDefault(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { - if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) { - Assume.assumeFalse(IS_UNDER_RAVENWOOD); - } - if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { + if (hasIgnoreUnderRavenwoodAnnotation(description)) { Assume.assumeFalse(IS_UNDER_RAVENWOOD); } - if (IS_UNDER_RAVENWOOD) { - RavenwoodRuleImpl.init(RavenwoodRule.this); - } + + RavenwoodRuleImpl.init(RavenwoodRule.this); try { base.evaluate(); } finally { - if (IS_UNDER_RAVENWOOD) { - RavenwoodRuleImpl.reset(RavenwoodRule.this); + RavenwoodRuleImpl.reset(RavenwoodRule.this); + } + } + }; + } + + /** + * Run the given {@link Statement} with probing enabled. All tests will be unconditionally + * run under Ravenwood to detect cases where a test is able to pass despite being marked as + * {@code IgnoreUnderRavenwood}. + */ + private Statement applyProbeIgnored(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + RavenwoodRuleImpl.init(RavenwoodRule.this); + try { + base.evaluate(); + } catch (Throwable t) { + if (hasIgnoreUnderRavenwoodAnnotation(description)) { + // This failure is expected, so eat the exception and report the + // assumption failure that test authors expect + Assume.assumeFalse(IS_UNDER_RAVENWOOD); } + throw t; + } finally { + RavenwoodRuleImpl.reset(RavenwoodRule.this); + } + + if (hasIgnoreUnderRavenwoodAnnotation(description) && IS_UNDER_RAVENWOOD) { + fail("Test was annotated with IgnoreUnderRavenwood, but it actually " + + "passed under Ravenwood; consider removing the annotation"); } } }; diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index fb71e9d1ac6f..0ff6a1ad846b 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -22,12 +22,10 @@ public class RavenwoodRuleImpl { } public static void init(RavenwoodRule rule) { - // Must be provided by impl to reference runtime internals - throw new UnsupportedOperationException(); + // No-op when running on a real device } public static void reset(RavenwoodRule rule) { - // Must be provided by impl to reference runtime internals - throw new UnsupportedOperationException(); + // No-op when running on a real device } } 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 ab678d93c665..b5130a1c4cfc 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -27,3 +27,10 @@ flag { description: "Mitigation for view state reset to empty causing no save dialog to show issue" bug: "297976948" } + +flag { + 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 09e79860b45c..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. @@ -1426,14 +1426,19 @@ public abstract class PackageManagerInternal { /** * Checks if package is quarantined for a specific user. + * + * @throws PackageManager.NameNotFoundException if the package is not found */ - public abstract boolean isPackageQuarantined(@NonNull String packageName, - @UserIdInt int userId); + public abstract boolean isPackageQuarantined(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; /** * Checks if package is stopped for a specific user. + * + * @throws PackageManager.NameNotFoundException if the package is not found */ - public abstract boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId); + public abstract boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; /** * Sends the PACKAGE_RESTARTED broadcast. 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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 71916843fe0b..df8f17ac9d7c 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -5175,6 +5175,8 @@ public final class ActiveServices { return null; } + final long startTimeNs = SystemClock.elapsedRealtimeNanos(); + if (DEBUG_SERVICE) { Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent + " fg=" + r.fgRequired); } @@ -5333,9 +5335,14 @@ public final class ActiveServices { bringDownServiceLocked(r, enqueueOomAdj); return msg; } + mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r, + hostingRecord, true); if (isolated) { r.isolationHostProc = app; } + } else { + mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r, + hostingRecord, false); } if (r.fgRequired) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ac173f3b5b7a..2ee39c577977 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -421,6 +421,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.os.TimeoutRecord; import com.android.internal.os.TransferPipe; import com.android.internal.os.Zygote; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.policy.AttributeCache; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; @@ -468,7 +469,6 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.SELinuxUtil; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.snapshot.PackageDataSnapshot; import com.android.server.power.stats.BatteryStatsImpl; import com.android.server.sdksandbox.SdkSandboxManagerLocal; @@ -1103,9 +1103,51 @@ public class ActivityManagerService extends IActivityManager.Stub private final ActivityMetricsLaunchObserver mActivityLaunchObserver = new ActivityMetricsLaunchObserver() { + + @Override + public void onIntentStarted(@NonNull Intent intent, long timestampNanos) { + synchronized (this) { + mProcessList.getAppStartInfoTracker().onIntentStarted(intent, timestampNanos); + } + } + @Override - public void onActivityLaunched(long id, ComponentName name, int temperature) { + public void onIntentFailed(long id) { + mProcessList.getAppStartInfoTracker().onIntentFailed(id); + } + + @Override + public void onActivityLaunched(long id, ComponentName name, int temperature, int userId) { mAppProfiler.onActivityLaunched(); + synchronized (ActivityManagerService.this) { + ProcessRecord record = null; + try { + record = getProcessRecordLocked(name.getPackageName(), mContext + .getPackageManager().getPackageUidAsUser(name.getPackageName(), 0, + userId)); + } catch (NameNotFoundException nnfe) { + // Ignore, record will be lost. + } + mProcessList.getAppStartInfoTracker().onActivityLaunched(id, name, temperature, + record); + } + } + + @Override + public void onActivityLaunchCancelled(long id) { + mProcessList.getAppStartInfoTracker().onActivityLaunchCancelled(id); + } + + @Override + public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos, + int launchMode) { + mProcessList.getAppStartInfoTracker().onActivityLaunchFinished(id, name, + timestampNanos, launchMode); + } + + @Override + public void onReportFullyDrawn(long id, long timestampNanos) { + mProcessList.getAppStartInfoTracker().onReportFullyDrawn(id, timestampNanos); } }; @@ -4488,13 +4530,13 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") private void attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) { - // Find the application record that is being attached... either via // the pid if we are running in multiple processes, or just pull the // next app record if we are emulating process with anonymous threads. ProcessRecord app; long startTime = SystemClock.uptimeMillis(); long bindApplicationTimeMillis; + long bindApplicationTimeNanos; if (pid != MY_PID && pid >= 0) { synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); @@ -4698,6 +4740,7 @@ public class ActivityManagerService extends IActivityManager.Stub checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); bindApplicationTimeMillis = SystemClock.uptimeMillis(); + bindApplicationTimeNanos = SystemClock.elapsedRealtimeNanos(); mAtmInternal.preBindApplication(app.getWindowProcessController()); final ActiveInstrumentation instr2 = app.getActiveInstrumentation(); if (mPlatformCompat != null) { @@ -4754,6 +4797,8 @@ public class ActivityManagerService extends IActivityManager.Stub } app.setBindApplicationTime(bindApplicationTimeMillis); + mProcessList.getAppStartInfoTracker() + .reportBindApplicationTimeNanos(app, bindApplicationTimeNanos); // Make app active after binding application or client may be running requests (e.g // starting activities) before it is ready. @@ -9799,12 +9844,12 @@ public class ActivityManagerService extends IActivityManager.Stub final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid, "getHistoricalProcessStartReasons"); if (uid != INVALID_UID) { - mProcessList.mAppStartInfoTracker.getStartInfo( + mProcessList.getAppStartInfoTracker().getStartInfo( packageName, userId, callingPid, maxNum, results); } } else { // If no package name is given, use the caller's uid as the filter uid. - mProcessList.mAppStartInfoTracker.getStartInfo( + mProcessList.getAppStartInfoTracker().getStartInfo( packageName, callingUid, callingPid, maxNum, results); } return new ParceledListSlice<ApplicationStartInfo>(results); @@ -9822,7 +9867,7 @@ public class ActivityManagerService extends IActivityManager.Stub } final int callingUid = Binder.getCallingUid(); - mProcessList.mAppStartInfoTracker.addStartInfoCompleteListener(listener, callingUid); + mProcessList.getAppStartInfoTracker().addStartInfoCompleteListener(listener, callingUid); } @@ -9836,7 +9881,7 @@ public class ActivityManagerService extends IActivityManager.Stub } final int callingUid = Binder.getCallingUid(); - mProcessList.mAppStartInfoTracker.clearStartInfoCompleteListener(callingUid, true); + mProcessList.getAppStartInfoTracker().clearStartInfoCompleteListener(callingUid, true); } @Override @@ -10138,7 +10183,7 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); - mProcessList.mAppStartInfoTracker.dumpHistoryProcessStartInfo(pw, dumpPackage); + mProcessList.getAppStartInfoTracker().dumpHistoryProcessStartInfo(pw, dumpPackage); pw.println("-------------------------------------------------------------------------------"); mProcessList.mAppExitInfoTracker.dumpHistoryProcessExitInfo(pw, dumpPackage); } @@ -10541,7 +10586,7 @@ public class ActivityManagerService extends IActivityManager.Stub dumpPackage = args[opti]; opti++; } - mProcessList.mAppStartInfoTracker.dumpHistoryProcessStartInfo(pw, dumpPackage); + mProcessList.getAppStartInfoTracker().dumpHistoryProcessStartInfo(pw, dumpPackage); } else if ("exit-info".equals(cmd)) { if (opti < args.length) { dumpPackage = args[opti]; @@ -13831,6 +13876,7 @@ public class ActivityManagerService extends IActivityManager.Stub // activity manager to announce its creation. public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId, @BackupDestination int backupDestination) { + long startTimeNs = SystemClock.elapsedRealtimeNanos(); if (DEBUG_BACKUP) { Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode + " targetUserId=" + targetUserId + " callingUid = " + Binder.getCallingUid() @@ -13906,15 +13952,20 @@ public class ActivityManagerService extends IActivityManager.Stub ? new ComponentName(app.packageName, app.backupAgentName) : new ComponentName("android", "FullBackupAgent"); - // startProcessLocked() returns existing proc's record if it's already running - ProcessRecord proc = startProcessLocked(app.processName, app, - false, 0, - new HostingRecord(HostingRecord.HOSTING_TYPE_BACKUP, hostingName), - ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false); + ProcessRecord proc = getProcessRecordLocked(app.processName, app.uid); + boolean isProcessStarted = proc != null; + if (!isProcessStarted) { + proc = startProcessLocked(app.processName, app, + false, 0, + new HostingRecord(HostingRecord.HOSTING_TYPE_BACKUP, hostingName), + ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false); + } if (proc == null) { Slog.e(TAG, "Unable to start backup agent process " + r); return false; } + mProcessList.getAppStartInfoTracker().handleProcessBackupStart(startTimeNs, proc, r, + !isProcessStarted); // If the app is a regular app (uid >= 10000) and not the system server or phone // process, etc, then mark it as being in full backup so that certain calls to the @@ -18741,8 +18792,12 @@ public class ActivityManagerService extends IActivityManager.Stub // If the process is known as top app, set a hint so when the process is // started, the top priority can be applied immediately to avoid cpu being // preempted by other processes before attaching the process of top app. - startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */, - new HostingRecord(hostingType, hostingName, isTop), + final long startTimeNs = SystemClock.elapsedRealtimeNanos(); + HostingRecord hostingRecord = + new HostingRecord(hostingType, hostingName, isTop); + ProcessRecord rec = getProcessRecordLocked(processName, info.uid); + ProcessRecord app = startProcessLocked(processName, info, knownToBeDead, + 0 /* intentFlags */, hostingRecord, ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */, false /* isolated */); } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index f3b2ef34ad6d..ae0cd65b2770 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -1362,7 +1362,7 @@ final class ActivityManagerShellCommand extends ShellCommand { } userId = user.id; } - mInternal.mProcessList.mAppStartInfoTracker + mInternal.mProcessList.getAppStartInfoTracker() .clearHistoryProcessStartInfo(packageName, userId); return 0; } diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java index edca74fae0e4..82e554e67b7e 100644 --- a/services/core/java/com/android/server/am/AppStartInfoTracker.java +++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java @@ -22,11 +22,12 @@ import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import android.app.ActivityOptions; +import android.annotation.NonNull; import android.app.ApplicationStartInfo; import android.app.Flags; import android.app.IApplicationStartInfoCompleteListener; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -138,6 +139,15 @@ public final class AppStartInfoTracker { /** The path to the historical proc start info file, persisted in the storage. */ @VisibleForTesting File mProcStartInfoFile; + + /** + * Temporary list of records that have not been completed. + * + * Key is timestamp of launch from {@link #ActivityMetricsLaunchObserver}. + */ + @GuardedBy("mLock") + private ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>(); + AppStartInfoTracker() { mCallbacks = new SparseArray<>(); mData = new ProcessMap<AppStartInfoContainer>(); @@ -174,68 +184,99 @@ public final class AppStartInfoTracker { }); } - void handleProcessColdStarted(long startTimeNs, HostingRecord hostingRecord, - ProcessRecord app) { + void onIntentStarted(@NonNull Intent intent, long timestampNanos) { synchronized (mLock) { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); - addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); - start.addStartupTimestamp( - ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs); - start.addStartupTimestamp( - ApplicationStartInfo.START_TIMESTAMP_FORK, app.getStartElapsedTime()); - start.setStartType(ApplicationStartInfo.START_TYPE_COLD); - start.setReason(ApplicationStartInfo.START_REASON_OTHER); - addStartInfoLocked(start); + start.setIntent(intent); + start.setStartType(ApplicationStartInfo.START_TYPE_UNSET); + start.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_LAUNCH, timestampNanos); + if (intent != null && intent.getCategories() != null + && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { + start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER); + } else { + start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY); + } + mInProgRecords.put(timestampNanos, start); } } - public void handleProcessActivityWarmOrHotStarted(long startTimeNs, - ActivityOptions activityOptions, Intent intent) { + void onIntentFailed(long id) { synchronized (mLock) { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(); - start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); - start.addStartupTimestamp( - ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs); - start.setIntent(intent); - start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER); - if (activityOptions != null) { - start.setProcessName(activityOptions.getPackageName()); + if (!mInProgRecords.containsKey(id)) { + return; } - start.setStartType(ApplicationStartInfo.START_TYPE_WARM); - if (intent != null && intent.getCategories() != null - && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { - start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER); + mInProgRecords.get(id).setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR); + mInProgRecords.remove(id); + } + } + + void onActivityLaunched(long id, ComponentName name, long temperature, ProcessRecord app) { + synchronized (mLock) { + if (!mEnabled) { + return; + } + if (!mInProgRecords.containsKey(id)) { + return; + } + if (app != null) { + ApplicationStartInfo info = mInProgRecords.get(id); + info.setStartType((int) temperature); + addBaseFieldsFromProcessRecord(info, app); + addStartInfoLocked(info); } else { - start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY); + mInProgRecords.remove(id); } - addStartInfoLocked(start); } } - public void handleProcessActivityStartedFromRecents(long startTimeNs, - ActivityOptions activityOptions) { + void onActivityLaunchCancelled(long id) { synchronized (mLock) { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(); - start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); - start.addStartupTimestamp( - ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs); - if (activityOptions != null) { - start.setIntent(activityOptions.getResultData()); - start.setProcessName(activityOptions.getPackageName()); + if (!mInProgRecords.containsKey(id)) { + return; } - start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER_RECENTS); - start.setStartType(ApplicationStartInfo.START_TYPE_WARM); - addStartInfoLocked(start); + ApplicationStartInfo info = mInProgRecords.get(id); + info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR); + mInProgRecords.remove(id); + } + } + + void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos, + int launchMode) { + synchronized (mLock) { + if (!mEnabled) { + return; + } + if (!mInProgRecords.containsKey(id)) { + return; + } + ApplicationStartInfo info = mInProgRecords.get(id); + info.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); + info.setLaunchMode(launchMode); + } + } + + void onReportFullyDrawn(long id, long timestampNanos) { + synchronized (mLock) { + if (!mEnabled) { + return; + } + if (!mInProgRecords.containsKey(id)) { + return; + } + ApplicationStartInfo info = mInProgRecords.get(id); + info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN, + timestampNanos); + mInProgRecords.remove(id); } } @@ -347,7 +388,8 @@ public final class AppStartInfoTracker { ApplicationStartInfo.START_TIMESTAMP_APPLICATION_ONCREATE); } - void reportBindApplicationTimeNanos(ProcessRecord app, long timeNs) { + /** Report a bind application timestamp to add to {@link ApplicationStartInfo}. */ + public void reportBindApplicationTimeNanos(ProcessRecord app, long timeNs) { addTimestampToStart(app, timeNs, ApplicationStartInfo.START_TIMESTAMP_BIND_APPLICATION); } diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 903cb7bcfaed..982076dd7c9a 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -30,7 +30,6 @@ import android.widget.WidgetFlags; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import java.util.ArrayList; import java.util.HashMap; @@ -164,12 +163,6 @@ final class CoreSettingsObserver extends ContentObserver { WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT)); sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION, - SystemUiDeviceConfigFlags.KEY_REMOTEVIEWS_ADAPTER_CONVERSION, boolean.class, - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT)); - - sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( TextFlags.NAMESPACE, TextFlags.ENABLE_NEW_CONTEXT_MENU, TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU, boolean.class, TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT)); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4ff34b1d7faa..3156e9da0ae9 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -498,7 +498,7 @@ public final class ProcessList { /** Manages the {@link android.app.ApplicationStartInfo} records. */ @GuardedBy("mAppStartInfoTracker") - final AppStartInfoTracker mAppStartInfoTracker = new AppStartInfoTracker(); + private final AppStartInfoTracker mAppStartInfoTracker = new AppStartInfoTracker(); /** * The currently running SDK sandbox processes for a uid. @@ -1523,6 +1523,10 @@ public final class ProcessList { return mCachedRestoreLevel; } + AppStartInfoTracker getAppStartInfoTracker() { + return mAppStartInfoTracker; + } + /** * Set the out-of-memory badness adjustment for a process. * If {@code pid <= 0}, this method will be a no-op. @@ -2572,6 +2576,7 @@ public final class ProcessList { boolean isSdkSandbox, int sdkSandboxUid, String sdkSandboxClientAppPackage, String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) { long startTime = SystemClock.uptimeMillis(); + final long startTimeNs = SystemClock.elapsedRealtimeNanos(); ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(processName, info.uid); 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/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index b1825380bbdc..32d5cf587e0c 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -152,7 +152,6 @@ public final class GameManagerService extends IGameManagerService.Stub { private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME = "game_mode_intervention.list"; - private final Context mContext; private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); @@ -184,6 +183,7 @@ public final class GameManagerService extends IGameManagerService.Stub { @GuardedBy("mUidObserverLock") private final Set<Integer> mForegroundGameUids = new HashSet<>(); private final GameManagerServiceSystemPropertiesWrapper mSysProps; + private float mGameDefaultFrameRateValue; @VisibleForTesting static class Injector { @@ -1559,6 +1559,10 @@ public final class GameManagerService extends IGameManagerService.Stub { mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, false); Slog.v(TAG, "Game power mode OFF (game manager service start/restart)"); mPowerManagerInternal.setPowerMode(Mode.GAME, false); + + mGameDefaultFrameRateValue = (float) mSysProps.getInt( + PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE, 60); + Slog.v(TAG, "Game Default Frame Rate : " + mGameDefaultFrameRateValue); } private void sendUserMessage(int userId, int what, String eventForLog, int delayMillis) { @@ -2217,8 +2221,7 @@ public final class GameManagerService extends IGameManagerService.Stub { } if (gameDefaultFrameRate()) { gameDefaultFrameRate = isGameDefaultFrameRateEnabled - ? (float) mSysProps.getInt( - PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE, 0) : 0.0f; + ? mGameDefaultFrameRateValue : 0.0f; } return gameDefaultFrameRate; } 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/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 44cb1367928d..290bb7e92c69 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -5106,7 +5106,7 @@ public class AudioService extends IAudioService.Stub private void setMasterMuteInternalNoCallerCheck( boolean mute, int flags, int userId, String eventSource) { if (DEBUG_VOL) { - Log.d(TAG, TextUtils.formatSimple("Master mute %s, %d, user=%d from %s", + Log.d(TAG, TextUtils.formatSimple("Master mute %s, flags 0x%x, userId=%d from %s", mute, flags, userId, eventSource)); } 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/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 5084b602bff2..578d9dc2aede 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -722,6 +722,7 @@ public class FaceService extends SystemService { if (Flags.faceVhalFeature() && Utils.isVirtualEnabled(getContext())) { if (virtualAt != -1) { //only virtual instance should be returned + Slog.i(TAG, "virtual hal is used"); return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt))); } else { Slog.e(TAG, "Could not find virtual interface while it is enabled"); 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 5ce0c8b384ef..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); } @@ -1057,6 +1082,7 @@ public class FingerprintService extends SystemService { if (Utils.isVirtualEnabled(getContext())) { if (virtualAt != -1) { //only virtual instance should be returned + Slog.i(TAG, "virtual hal is used"); return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt))); } else { Slog.e(TAG, "Could not find virtual interface while it is enabled"); @@ -1085,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/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 1e5e147749a2..df179a9b6d65 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -1294,8 +1294,8 @@ public class SyncManager { if (android.content.pm.Flags.stayStopped()) { try { return mPackageManagerInternal.isPackageStopped(packageName, userId); - } catch (IllegalArgumentException e) { - Log.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName); + } catch (NameNotFoundException e) { + Log.e(TAG, "Couldn't determine stopped state for unknown package: " + packageName); } } return false; 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/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 168715713f8d..5831b29437dc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -194,10 +194,14 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { - if (result != HdmiControlManager.RESULT_SUCCESS) { + if (!mService.getLocalActiveSource().isValid() + && result != HdmiControlManager.RESULT_SUCCESS) { mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( getDeviceInfo().getLogicalAddress(), getDeviceInfo().getPhysicalAddress())); + updateActiveSource(getDeviceInfo().getLogicalAddress(), + getDeviceInfo().getPhysicalAddress(), + "RequestActiveSourceAction#finishWithCallback()"); } } })); @@ -257,6 +261,7 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (isAlreadyActiveSource(targetDevice, targetAddress, callback)) { return; } + removeAction(RequestActiveSourceAction.class); if (targetAddress == Constants.ADDR_INTERNAL) { handleSelectInternalSource(); // Switching to internal source is always successful even when CEC control is disabled. diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index f526dbe9c66d..4089a81dfc20 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -215,10 +215,20 @@ public abstract class InputMethodManagerInternal { /** * Switch the keyboard layout in response to a keyboard shortcut. * - * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the - * previous subtype + * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the + * previous subtype + * @param displayId the display to which the keyboard layout switch shortcut is + * dispatched. Note that there is no guarantee that an IME is + * associated with this display. This is more or less than a hint for + * cases when no IME is running for the given targetWindowToken. There + * is a longstanding discussion whether we should allow users to + * rotate keyboard layout even when there is no edit field, and this + * displayID would be helpful for such a situation. + * @param targetWindowToken the window token to which other keys are being sent while handling + * this shortcut. */ - public abstract void switchKeyboardLayout(int direction); + public abstract void onSwitchKeyboardLayoutShortcut(int direction, int displayId, + IBinder targetWindowToken); /** * Returns true if any InputConnection is currently active. @@ -314,7 +324,8 @@ public abstract class InputMethodManagerInternal { } @Override - public void switchKeyboardLayout(int direction) { + public void onSwitchKeyboardLayoutShortcut(int direction, int displayId, + IBinder targetWindowToken) { } @Override diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 30e9f5bd2a14..16e043cfb64d 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5763,7 +5763,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public void switchKeyboardLayout(int direction) { + public void onSwitchKeyboardLayoutShortcut(int direction, int displayId, + IBinder targetWindowToken) { synchronized (ImfLock.class) { switchKeyboardLayoutLocked(direction); } @@ -6498,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/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index ec858ee296e1..f2dcba45e312 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -59,6 +59,7 @@ import android.util.apk.SourceStampVerifier; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; @@ -67,7 +68,6 @@ import com.android.server.integrity.model.IntegrityCheckResult; import com.android.server.integrity.model.RuleMetadata; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.parsing.PackageParser2; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.ByteArrayInputStream; import java.io.File; diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index bab3cbea108e..9c27c22dfd00 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -18,6 +18,7 @@ package com.android.server.location.contexthub; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.contexthub.HostEndpointInfo; +import android.hardware.contexthub.MessageDeliveryStatus; import android.hardware.contexthub.NanSessionRequest; import android.hardware.contexthub.V1_0.ContextHub; import android.hardware.contexthub.V1_0.ContextHubMsg; @@ -467,6 +468,11 @@ public abstract class IContextHubWrapper { // TODO(271471342): Implement } + public void handleMessageDeliveryStatus(char hostEndPointId, + MessageDeliveryStatus messageDeliveryStatus) { + // TODO(b/312417087): Implement reliable message support + } + public byte[] getUuid() { return UUID; } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 42c2548264d4..0c2eee5aebbe 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -209,7 +209,7 @@ import javax.crypto.spec.GCMParameterSpec; * <li>Protect each user's data using their SP. For example, use the SP to encrypt/decrypt the * user's credential-encrypted (CE) key for file-based encryption (FBE).</li> * - * <li>Generate, protect, and use profile passwords for managed profiles.</li> + * <li>Generate, protect, and use unified profile passwords.</li> * * <li>Support unlocking the SP by alternative means: resume-on-reboot (reboot escrow) for easier * OTA updates, and escrow tokens when set up by the Device Policy Controller (DPC).</li> @@ -287,7 +287,7 @@ public class LockSettingsService extends ILockSettings.Stub { private final java.security.KeyStore mJavaKeyStore; private final RecoverableKeyStoreManager mRecoverableKeyStoreManager; - private ManagedProfilePasswordCache mManagedProfilePasswordCache; + private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache; private final RebootEscrowManager mRebootEscrowManager; @@ -404,7 +404,8 @@ public class LockSettingsService extends ILockSettings.Stub { for (int i = 0; i < newPasswordChars.length; i++) { newPassword[i] = (byte) newPasswordChars[i]; } - LockscreenCredential credential = LockscreenCredential.createManagedPassword(newPassword); + LockscreenCredential credential = + LockscreenCredential.createUnifiedProfilePassword(newPassword); Arrays.fill(newPasswordChars, '\u0000'); Arrays.fill(newPassword, (byte) 0); Arrays.fill(randomLockSeed, (byte) 0); @@ -424,7 +425,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (!isCredentialSharableWithParent(profileUserId)) { return; } - // Do not tie profile when work challenge is enabled + // Do not tie profile when separate challenge is enabled if (getSeparateProfileChallengeEnabledInternal(profileUserId)) { return; } @@ -462,7 +463,7 @@ public class LockSettingsService extends ILockSettings.Stub { setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); tieProfileLockToParent(profileUserId, parent.id, unifiedProfilePassword); - mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, + mUnifiedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, parentSid); } } @@ -620,9 +621,9 @@ public class LockSettingsService extends ILockSettings.Stub { } } - public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache( + public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache( java.security.KeyStore ks) { - return new ManagedProfilePasswordCache(ks); + return new UnifiedProfilePasswordCache(ks); } public boolean isHeadlessSystemUserMode() { @@ -665,7 +666,7 @@ public class LockSettingsService extends ILockSettings.Stub { mGatekeeperPasswords = new LongSparseArray<>(); mSpManager = injector.getSyntheticPasswordManager(mStorage); - mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore); + mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore); mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), @@ -689,8 +690,8 @@ public class LockSettingsService extends ILockSettings.Stub { } /** - * If the account is credential-encrypted, show notification requesting the user to unlock the - * device. + * If the user is a managed profile whose credential-encrypted storage is locked, show a + * notification requesting the user to unlock the device. */ private void maybeShowEncryptionNotificationForUser(@UserIdInt int userId, String reason) { final UserInfo user = mUserManager.getUserInfo(userId); @@ -846,7 +847,7 @@ public class LockSettingsService extends ILockSettings.Stub { mHandler.post(new Runnable() { @Override public void run() { - // Hide notification first, as tie managed profile lock takes time + // Hide notification first, as tie profile lock takes time hideEncryptionNotification(new UserHandle(userId)); if (isCredentialSharableWithParent(userId)) { @@ -1458,13 +1459,13 @@ public class LockSettingsService extends ILockSettings.Stub { cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv)); decryptionResult = cipher.doFinal(encryptedPassword); - LockscreenCredential credential = LockscreenCredential.createManagedPassword( + LockscreenCredential credential = LockscreenCredential.createUnifiedProfilePassword( decryptionResult); Arrays.fill(decryptionResult, (byte) 0); try { long parentSid = getGateKeeperService().getSecureUserId( mUserManager.getProfileParent(userId).id); - mManagedProfilePasswordCache.storePassword(userId, credential, parentSid); + mUnifiedProfilePasswordCache.storePassword(userId, credential, parentSid); } catch (RemoteException e) { Slogf.w(TAG, "Failed to talk to GateKeeper service", e); } @@ -1550,7 +1551,7 @@ public class LockSettingsService extends ILockSettings.Stub { // so it goes into the cache getDecryptedPasswordForTiedProfile(profile.id); } catch (GeneralSecurityException | IOException e) { - Slog.d(TAG, "Cache work profile password failed", e); + Slog.d(TAG, "Cache unified profile password failed", e); } } } @@ -1604,19 +1605,19 @@ public class LockSettingsService extends ILockSettings.Stub { } /** - * Synchronize all profile's work challenge of the given user if it's unified: tie or clear them + * Synchronize all profile's challenge of the given user if it's unified: tie or clear them * depending on the parent user's secure state. * - * When clearing tied work challenges, a pre-computed password table for profiles are required, - * since changing password for profiles requires existing password, and existing passwords can - * only be computed before the parent user's password is cleared. + * When clearing tied challenges, a pre-computed password table for profiles are required, since + * changing password for profiles requires existing password, and existing passwords can only be + * computed before the parent user's password is cleared. * * Strictly this is a recursive function, since setLockCredentialInternal ends up calling this * method again on profiles. However the recursion is guaranteed to terminate as this method * terminates when the user is a profile that shares lock credentials with parent. * (e.g. managed and clone profile). */ - private void synchronizeUnifiedWorkChallengeForProfiles(int userId, + private void synchronizeUnifiedChallengeForProfiles(int userId, Map<Integer, LockscreenCredential> profilePasswordMap) { if (isCredentialSharableWithParent(userId)) { return; @@ -1635,7 +1636,7 @@ public class LockSettingsService extends ILockSettings.Stub { tieProfileLockIfNecessary(profileUserId, LockscreenCredential.createNone()); } else { - // We use cached work profile password computed before clearing the parent's + // We use cached profile password computed before clearing the parent's // credential, otherwise they get lost if (profilePasswordMap != null && profilePasswordMap.containsKey(profileUserId)) { @@ -1777,7 +1778,7 @@ public class LockSettingsService extends ILockSettings.Stub { notifyPasswordChanged(credential, userId); } if (isCredentialSharableWithParent(userId)) { - // Make sure the profile doesn't get locked straight after setting work challenge. + // Make sure the profile doesn't get locked straight after setting challenge. setDeviceUnlockedForUser(userId); } notifySeparateProfileChallengeChanged(userId); @@ -2368,7 +2369,7 @@ public class LockSettingsService extends ILockSettings.Stub { } try { - // Unlock work profile, and work profile with unified lock must use password only + // Unlock profile with unified lock return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId), userId, null /* progressCallback */, flags); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException @@ -2492,7 +2493,7 @@ public class LockSettingsService extends ILockSettings.Stub { mStrongAuth.removeUser(userId); AndroidKeyStoreMaintenance.onUserRemoved(userId); - mManagedProfilePasswordCache.removePassword(userId); + mUnifiedProfilePasswordCache.removePassword(userId); gateKeeperClearSecureUserId(userId); removeKeystoreProfileKey(userId); @@ -2982,7 +2983,7 @@ public class LockSettingsService extends ILockSettings.Stub { credential, sp, userId); final Map<Integer, LockscreenCredential> profilePasswords; if (!credential.isNone()) { - // not needed by synchronizeUnifiedWorkChallengeForProfiles() + // not needed by synchronizeUnifiedChallengeForProfiles() profilePasswords = null; if (!mSpManager.hasSidForUser(userId)) { @@ -2993,8 +2994,8 @@ public class LockSettingsService extends ILockSettings.Stub { } } } else { - // Cache all profile password if they use unified work challenge. This will later be - // used to clear the profile's password in synchronizeUnifiedWorkChallengeForProfiles() + // Cache all profile password if they use unified challenge. This will later be used to + // clear the profile's password in synchronizeUnifiedChallengeForProfiles(). profilePasswords = getDecryptedPasswordsForAllTiedProfiles(userId); mSpManager.clearSidForUser(userId); @@ -3010,10 +3011,10 @@ public class LockSettingsService extends ILockSettings.Stub { } setCurrentLskfBasedProtectorId(newProtectorId, userId); LockPatternUtils.invalidateCredentialTypeCache(); - synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords); + synchronizeUnifiedChallengeForProfiles(userId, profilePasswords); setUserPasswordMetrics(credential, userId); - mManagedProfilePasswordCache.removePassword(userId); + mUnifiedProfilePasswordCache.removePassword(userId); if (savedCredentialType != CREDENTIAL_TYPE_NONE) { mSpManager.destroyAllWeakTokenBasedProtectors(userId); } @@ -3114,7 +3115,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { currentCredential = getDecryptedPasswordForTiedProfile(userId); } catch (Exception e) { - Slog.e(TAG, "Failed to get work profile credential", e); + Slog.e(TAG, "Failed to get unified profile password", e); return null; } } @@ -3284,7 +3285,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean tryUnlockWithCachedUnifiedChallenge(int userId) { checkPasswordReadPermission(); - try (LockscreenCredential cred = mManagedProfilePasswordCache.retrievePassword(userId)) { + try (LockscreenCredential cred = mUnifiedProfilePasswordCache.retrievePassword(userId)) { if (cred == null) { return false; } @@ -3296,7 +3297,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void removeCachedUnifiedChallenge(int userId) { checkWritePermission(); - mManagedProfilePasswordCache.removePassword(userId); + mUnifiedProfilePasswordCache.removePassword(userId); } static String timestampToString(long timestamp) { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 1e8b387fc189..6d123ccebc7c 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -501,10 +501,10 @@ class LockSettingsStorage { final UserInfo parentInfo = um.getProfileParent(userId); if (parentInfo == null) { - // This user owns its lock settings files - safe to delete them + // Delete files specific to non-profile users. deleteFile(getRebootEscrowFile(userId)); } else { - // Managed profile + // Delete files specific to profile users. removeChildProfileLock(userId); } diff --git a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java index 1298fe8f07a4..21caf76d30d0 100644 --- a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +++ b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java @@ -43,30 +43,31 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; /** - * Caches *unified* work challenge for managed profiles. The cached credential is encrypted using - * a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified - * work challenge is normally secured. - * - * <p> The cache is filled whenever the managed profile's unified challenge is created or derived - * (as part of the parent user's credential verification flow). It's removed when the profile is - * deleted or a (separate) lockscreen credential is explicitly set on the profile. There is also - * an ADB command to evict the cache "cmd lock_settings remove-cache --user X", to assist - * development and testing. - - * <p> The encrypted credential is stored in-memory only so the cache does not persist across - * reboots. + * An in-memory cache for unified profile passwords. A "unified profile password" is the random + * password that the system automatically generates and manages for each profile that uses a unified + * challenge and where the parent user has a secure lock screen. + * <p> + * Each password in this cache is encrypted by a Keystore key that is auth-bound to the parent user. + * This is very similar to how the password is protected on-disk, but the in-memory cache uses a + * much longer timeout on the keys: 7 days instead of 30 seconds. This enables use cases like + * unpausing work apps without requiring authentication as frequently. + * <p> + * Unified profile passwords are cached when they are created, or when they are decrypted as part of + * the parent user's LSKF verification flow. They are removed when the profile is deleted or when a + * separate challenge is explicitly set on the profile. There is also an ADB command to evict a + * cached password, "locksettings remove-cache --user X", to assist development and testing. */ @VisibleForTesting // public visibility is needed for Mockito -public class ManagedProfilePasswordCache { +public class UnifiedProfilePasswordCache { - private static final String TAG = "ManagedProfilePasswordCache"; + private static final String TAG = "UnifiedProfilePasswordCache"; private static final int KEY_LENGTH = 256; private static final int CACHE_TIMEOUT_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>(); private final KeyStore mKeyStore; - public ManagedProfilePasswordCache(KeyStore keyStore) { + public UnifiedProfilePasswordCache(KeyStore keyStore) { mKeyStore = keyStore; } @@ -151,7 +152,8 @@ public class ManagedProfilePasswordCache { Slog.d(TAG, "Cannot decrypt", e); return null; } - LockscreenCredential result = LockscreenCredential.createManagedPassword(credential); + LockscreenCredential result = + LockscreenCredential.createUnifiedProfilePassword(credential); Arrays.fill(credential, (byte) 0); return result; } 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 a62d8b8bc032..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; @@ -5406,6 +5407,7 @@ public class NotificationManagerService extends SystemService { private void validateAutomaticZenRule(AutomaticZenRule rule) { Objects.requireNonNull(rule, "automaticZenRule is null"); Objects.requireNonNull(rule.getName(), "Name is null"); + rule.validate(); if (rule.getOwner() == null && rule.getConfigurationActivity() == null) { throw new NullPointerException( @@ -5463,6 +5465,7 @@ public class NotificationManagerService extends SystemService { public void setAutomaticZenRuleState(String id, Condition condition) { Objects.requireNonNull(id, "id is null"); Objects.requireNonNull(condition, "Condition is null"); + condition.validate(); enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState"); @@ -6962,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/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 7ec94c315798..3f8b5952a1bc 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -627,6 +627,7 @@ public class ZenModeHelper { try { ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0); rule.name = applicationInfo.loadLabel(mPm).toString(); + rule.iconResName = drawableResIdToResName(pkg, applicationInfo.icon); } catch (PackageManager.NameNotFoundException e) { // Should not happen, since it's the app calling us (?) Log.w(TAG, "Package not found for creating implicit zen rule"); @@ -634,6 +635,9 @@ public class ZenModeHelper { } }); + rule.type = AutomaticZenRule.TYPE_OTHER; + rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description, + rule.name); rule.condition = null; rule.conditionId = new Uri.Builder() .scheme(Condition.SCHEME) diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java index f985b5b64e21..b9b09fb0e84c 100644 --- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java @@ -754,9 +754,9 @@ public class PersistentDataBlockService extends SystemService { } }; - private PersistentDataBlockManagerInternal mInternalService = - new PersistentDataBlockManagerInternal() { + private InternalService mInternalService = new InternalService(); + private class InternalService implements PersistentDataBlockManagerInternal { @Override public void setFrpCredentialHandle(byte[] handle) { writeInternal(handle, getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE); 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 bd725ed49f3c..482807c397ea 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -508,14 +508,19 @@ public interface Computer extends PackageDataSnapshot { boolean getApplicationHiddenSettingAsUser(@NonNull String packageName, @UserIdInt int userId); - boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId); + boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; - boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId); + boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; /** Check if the package is in a stopped state for a given user. */ - boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId); + 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 2dc3fb7464fd..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<>(); } @@ -4980,40 +4977,45 @@ public class ComputerEngine implements Computer { } } - private PackageUserStateInternal getUserStageOrDefaultForUser(@NonNull String packageName, - int userId) { + private PackageUserStateInternal getUserStateOrDefaultForUser(@NonNull String packageName, + int userId) throws PackageManager.NameNotFoundException { final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "when asking about packages for user " + userId); final PackageStateInternal ps = mSettings.getPackage(packageName); if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { - throw new IllegalArgumentException("Unknown target package: " + packageName); + throw new PackageManager.NameNotFoundException(packageName); } return ps.getUserStateOrDefault(userId); } @Override - public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) { - return getUserStageOrDefaultForUser(packageName, userId).isSuspended(); + public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) + throws PackageManager.NameNotFoundException { + return getUserStateOrDefaultForUser(packageName, userId).isSuspended(); } @Override - public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) { - return getUserStageOrDefaultForUser(packageName, userId).isQuarantined(); + public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { + return getUserStateOrDefaultForUser(packageName, userId).isQuarantined(); } @Override - public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) { - return getUserStageOrDefaultForUser(packageName, userId).isStopped(); + public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { + return getUserStateOrDefaultForUser(packageName, userId).isStopped(); } @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/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java index 24a33f1949fa..e3bbd2d8d17e 100644 --- a/services/core/java/com/android/server/pm/IPackageManagerBase.java +++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java @@ -951,20 +951,32 @@ public abstract class IPackageManagerBase extends IPackageManager.Stub { @Deprecated public final boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId) { - return snapshot().isPackageSuspendedForUser(packageName, userId); + try { + return snapshot().isPackageSuspendedForUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("Unknown target package: " + packageName); + } } @Override @Deprecated public final boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) { - return snapshot().isPackageQuarantinedForUser(packageName, userId); + try { + return snapshot().isPackageQuarantinedForUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("Unknown target package: " + packageName); + } } @Override public final boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) { - return snapshot().isPackageStoppedForUser(packageName, userId); + try { + return snapshot().isPackageStoppedForUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("Unknown target package: " + packageName); + } } @Override diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java index 5c4447eb99a4..3b9f9c804e27 100644 --- a/services/core/java/com/android/server/pm/InitAppsHelper.java +++ b/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME; import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; @@ -30,7 +31,6 @@ import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX; import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN; import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS; import static com.android.server.pm.PackageManagerService.TAG; -import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX; import android.annotation.NonNull; import android.annotation.Nullable; @@ -46,12 +46,12 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.om.OverlayConfig; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.EventLogTags; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.WatchedArrayMap; import java.io.File; diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index f1c062763183..0fa7aa5473b2 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -154,12 +154,16 @@ import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.F2fsUtils; +import com.android.internal.pm.parsing.PackageParserException; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.security.VerityUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; @@ -181,8 +185,6 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.SharedLibraryWrapper; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.security.FileIntegrityService; import com.android.server.utils.WatchedArrayMap; @@ -1165,7 +1167,7 @@ final class InstallPackageHelper { parseFlags); archivedPackage = request.getPackageLite().getArchivedPackage(); } - } catch (PackageManagerException e) { + } catch (PackageManagerException | PackageParserException e) { throw new PrepareFailure("Failed parse during installPackageLI", e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -2450,9 +2452,9 @@ final class InstallPackageHelper { // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088) mAppDataHelper.prepareAppDataPostCommitLIF(ps, 0, installRequest.getNewUsers()); if (installRequest.isClearCodeCache()) { - mAppDataHelper.clearAppDataLeafLIF(packageName, ps.getVolumeUuid(), - UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE - | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL + | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); } if (installRequest.isInstallReplace() && pkg != null) { mDexManager.notifyPackageUpdated(packageName, @@ -2910,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; @@ -3856,7 +3859,7 @@ final class InstallPackageHelper { synchronized (mPm.mLock) { platformPackage = mPm.getPlatformPackage(); - var isSystemApp = AndroidPackageUtils.isSystem(parsedPackage); + var isSystemApp = AndroidPackageLegacyUtils.isSystem(parsedPackage); final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr( AndroidPackageUtils.getRealPackageOrNull(parsedPackage, isSystemApp)); realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName, @@ -4574,7 +4577,7 @@ final class InstallPackageHelper { private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg) throws PackageManagerException { - if (!AndroidPackageUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) { + if (!AndroidPackageLegacyUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) { SharedUserSetting sharedUserSetting = null; try { synchronized (mPm.mLock) { @@ -4612,7 +4615,7 @@ final class InstallPackageHelper { final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0) && ScanPackageUtils.getVendorPartitionVersion() < 28; if (((scanFlags & SCAN_AS_PRIVILEGED) == 0) - && !AndroidPackageUtils.isPrivileged(pkg) + && !AndroidPackageLegacyUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null) && !skipVendorPrivilegeScan) { SharedUserSetting sharedUserSetting = null; diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 5494bd9808c8..ee780d99b6b6 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -51,12 +51,12 @@ import android.util.ExceptionUtils; import android.util.Slog; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.ArrayUtils; import com.android.server.art.model.DexoptResult; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.File; import java.util.ArrayList; @@ -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 58c1c0564f95..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); } @@ -1575,6 +1575,57 @@ public class LauncherAppsService extends SystemService { } @Override + public List<String> getPreInstalledSystemPackages(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 preinstalled packages for another user")) { + return null; + } + final long identity = Binder.clearCallingIdentity(); + try { + String userType = mUm.getUserInfo(user.getIdentifier()).userType; + Set<String> preInstalledPackages = mUm.getPreInstallableSystemPackages(userType); + if (preInstalledPackages == null) { + return new ArrayList<>(); + } + return List.copyOf(preInstalledPackages); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @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 e3bab3f243c3..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, @@ -323,6 +323,7 @@ public class PackageArchiver { PackageStateInternal ps = getPackageState(packageName, snapshot, Binder.getCallingUid(), userId); verifyNotSystemApp(ps.getFlags()); + verifyInstalled(ps, userId); String responsibleInstallerPackage = getResponsibleInstallerPackage(ps); verifyInstaller(responsibleInstallerPackage, userId); ApplicationInfo installerInfo = snapshot.getApplicationInfo( @@ -476,6 +477,14 @@ public class PackageArchiver { } } + private void verifyInstalled(PackageStateInternal ps, int userId) + throws PackageManager.NameNotFoundException { + if (!ps.getUserStateOrDefault(userId).isInstalled()) { + throw new PackageManager.NameNotFoundException( + TextUtils.formatSimple("%s is not installed.", ps.getPackageName())); + } + } + /** * Returns true if the app is archivable. */ @@ -519,11 +528,11 @@ public class PackageArchiver { /** * Returns true if user has opted the app out of archiving through system settings. */ - // TODO(b/304256918) Switch this to a separate OP code for archiving. private boolean isAppOptedOutOfArchiving(String packageName, int uid) { return Binder.withCleanCallingIdentity(() -> - getAppOpsManager().checkOp(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, - uid, packageName) == MODE_IGNORED); + getAppOpsManager().checkOpNoThrow( + AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, packageName) + == MODE_IGNORED); } private void verifyOptOutStatus(String packageName, int uid) @@ -676,23 +685,51 @@ public class PackageArchiver { PackageStateInternal ps; try { ps = getPackageState(packageName, snapshot, callingUid, userId); - snapshot.enforceCrossUserPermission(callingUid, userId, true, false, - "getArchivedAppIcon"); - verifyArchived(ps, userId); } catch (PackageManager.NameNotFoundException e) { - throw new ParcelableException(e); + Slog.e(TAG, TextUtils.formatSimple("Package %s couldn't be found.", packageName), e); + return null; } - List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault( - userId).getArchiveState().getActivityInfos(); - if (activityInfos.size() == 0) { + ArchiveState archiveState = getAnyArchiveState(ps, userId); + if (archiveState == null || archiveState.getActivityInfos().size() == 0) { return null; } // TODO(b/298452477) Handle monochrome icons. // In the rare case the archived app defined more than two launcher activities, we choose // the first one arbitrarily. - return includeCloudOverlay(decodeIcon(activityInfos.get(0))); + return includeCloudOverlay(decodeIcon(archiveState.getActivityInfos().get(0))); + } + + /** + * This method first checks the ArchiveState for the provided userId and then tries to fallback + * to other users if the current user is not archived. + * + * <p> This fallback behaviour is required for archived apps to fit into the multi-user world + * where APKs are shared across users. E.g. current ways of fetching icons for apps that are + * only installed on the work profile also work when executed on the personal profile if you're + * using {@link PackageManager#MATCH_UNINSTALLED_PACKAGES}. Resource fetching from APKs is for + * the most part userId-agnostic, which we need to mimic here in order for existing methods + * like {@link PackageManager#getApplicationIcon} to continue working. + * + * @return {@link ArchiveState} for {@code userId} if present. If not present, false back to an + * arbitrary userId. If no user is archived, returns null. + */ + @Nullable + private ArchiveState getAnyArchiveState(PackageStateInternal ps, int userId) { + PackageUserStateInternal userState = ps.getUserStateOrDefault(userId); + if (isArchived(userState)) { + return userState.getArchiveState(); + } + + for (int i = 0; i < ps.getUserStates().size(); i++) { + userState = ps.getUserStates().valueAt(i); + if (isArchived(userState)) { + return userState.getArchiveState(); + } + } + + return null; } @VisibleForTesting 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/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 47d1df5df1c0..4adb60c34c52 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -168,6 +168,7 @@ import com.android.internal.content.InstallLocationUtils; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.os.SomeArgs; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.security.VerityUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; @@ -181,7 +182,6 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.dex.DexManager; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import libcore.io.IoUtils; import libcore.util.EmptyArray; diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 1e7d0437d99e..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 @@ -762,13 +767,14 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { } @Override - public boolean isPackageQuarantined(@NonNull String packageName, - @UserIdInt int userId) { + public boolean isPackageQuarantined(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { return snapshot().isPackageQuarantinedForUser(packageName, userId); } @Override - public boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId) { + public boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { return snapshot().isPackageStoppedForUser(packageName, userId); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c5b006c4c77d..bc441b84c58b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -185,6 +185,7 @@ import com.android.internal.pm.parsing.pkg.AndroidPackageInternal; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.pm.pkg.component.ParsedMainComponent; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.telephony.CarrierAppUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; @@ -238,7 +239,6 @@ import com.android.server.pm.pkg.SharedUserApi; import com.android.server.pm.pkg.mutate.PackageStateMutator; import com.android.server.pm.pkg.mutate.PackageStateWrite; import com.android.server.pm.pkg.mutate.PackageUserStateWrite; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.resolution.ComponentResolver; import com.android.server.pm.resolution.ComponentResolverApi; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; @@ -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. @@ -2013,6 +2014,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService public boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); } + + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return SystemConfig.getInstance().getHiddenApiWhitelistedApps(); + } + + @Override + public Set<String> getInstallConstraintsAllowlist() { + return SystemConfig.getInstance().getInstallConstraintsAllowlist(); + } }; // CHECKSTYLE:ON IndentationCheck @@ -3127,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 @@ -3135,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; } @@ -3158,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) { @@ -5242,14 +5256,19 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String getSuspendingPackage(String packageName, int userId) { - final int callingUid = Binder.getCallingUid(); - final Computer snapshot = snapshot(); - // This will do visibility checks as well. - if (!snapshot.isPackageSuspendedForUser(packageName, userId)) { + try { + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshot(); + // This will do visibility checks as well. + if (!snapshot.isPackageSuspendedForUser(packageName, userId)) { + return null; + } + final UserPackage suspender = mSuspendPackageHelper.getSuspendingPackage( + snapshot, packageName, userId, callingUid); + return suspender != null ? suspender.packageName : null; + } catch (PackageManager.NameNotFoundException e) { return null; } - return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId, - callingUid); } @Override @@ -6184,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()) { @@ -6193,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); } @@ -6639,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); } @@ -6653,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 bb0017c80d6d..b6de0e5c030f 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -20,19 +20,23 @@ 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; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.WatchedLongSparseArray; import java.util.ArrayList; @@ -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 109d7ba1d29e..f42b43f5aeef 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -44,12 +44,12 @@ import android.util.Slog; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.util.ArrayUtils; import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.parsing.PackageCacher; -import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; @@ -167,7 +167,7 @@ final class RemovePackageHelper { if (removedPackage != null) { // TODO: Use PackageState for isSystem cleanPackageDataStructuresLILPw(removedPackage, - AndroidPackageUtils.isSystem(removedPackage), chatty); + AndroidPackageLegacyUtils.isSystem(removedPackage), chatty); } } } @@ -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/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java index 31a63e07b66c..5c6d61e3eaf9 100644 --- a/services/core/java/com/android/server/pm/ScanPackageUtils.java +++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java @@ -74,11 +74,13 @@ import android.util.jar.StrictJarFile; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.pm.pkg.component.ParsedService; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.ArrayUtils; import com.android.server.SystemConfig; import com.android.server.pm.parsing.PackageInfoUtils; @@ -86,8 +88,6 @@ import com.android.server.pm.parsing.library.PackageBackwardCompatibility; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateUtils; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.WatchedArraySet; import dalvik.system.VMRuntime; diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java index 37cf30bd63fe..41e2a3f3d318 100644 --- a/services/core/java/com/android/server/pm/ScanRequest.java +++ b/services/core/java/com/android/server/pm/ScanRequest.java @@ -22,8 +22,8 @@ import android.os.UserHandle; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; /** A package to be scanned */ @VisibleForTesting diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 75d88da059b5..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; @@ -89,6 +90,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedPermission; @@ -106,7 +108,6 @@ import com.android.server.LocalServices; import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.parsing.PackageInfoUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.permission.LegacyPermissionSettings; import com.android.server.pm.permission.LegacyPermissionState; @@ -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/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index dddc6b0fbb7a..5c0a15a28285 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -25,14 +25,14 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.proto.ProtoOutputStream; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedProcess; +import com.android.internal.pm.pkg.component.ParsedProcessImpl; import com.android.internal.util.ArrayUtils; import com.android.server.pm.permission.LegacyPermissionState; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.SharedUserApi; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ParsedProcessImpl; import com.android.server.utils.SnapshotCache; import com.android.server.utils.Watchable; import com.android.server.utils.WatchedArraySet; diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index b607502baada..7d87d1b27da0 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -47,11 +47,11 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.policy.AttributeCache; import com.android.internal.util.IndentingPrintWriter; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.File; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index fe8c12c8e232..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,12 +422,25 @@ public final class SuspendPackageHelper { return null; } - String suspendingPackage = null; + UserPackage suspendingPackage = null; + UserPackage suspendedBySystem = null; + UserPackage qasPackage = null; for (int i = 0; i < userState.getSuspendParams().size(); i++) { suspendingPackage = userState.getSuspendParams().keyAt(i); - if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { - return suspendingPackage; + var suspendParams = userState.getSuspendParams().valueAt(i); + if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) { + suspendedBySystem = suspendingPackage; } + if (suspendParams.isQuarantined() && qasPackage == null) { + qasPackage = suspendingPackage; + } + } + // Precedence: quarantined, then system, then suspending. + if (qasPackage != null) { + return qasPackage; + } + if (suspendedBySystem != null) { + return suspendedBySystem; } return suspendingPackage; } @@ -438,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); @@ -451,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; @@ -480,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; @@ -542,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; @@ -559,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 @@ -601,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 = @@ -622,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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index a7b52f4e7a58..c48eccf2aac5 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -532,9 +532,10 @@ public class UserManagerService extends IUserManager.Stub { } final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT, android.content.IntentSender.class); final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL); + final String callingPackage = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); // Call setQuietModeEnabled on bg thread to avoid ANR BackgroundThread.getHandler().post(() -> - setQuietModeEnabled(userId, false, target, /* callingPackage */ null)); + setQuietModeEnabled(userId, false, target, callingPackage)); } }; @@ -1410,7 +1411,7 @@ public class UserManagerService extends IUserManager.Stub { if (onlyIfCredentialNotRequired) { return false; } - showConfirmCredentialToDisableQuietMode(userId, target); + showConfirmCredentialToDisableQuietMode(userId, target, callingPackage); return false; } } @@ -1434,7 +1435,7 @@ public class UserManagerService extends IUserManager.Stub { if (onlyIfCredentialNotRequired) { return false; } - showConfirmCredentialToDisableQuietMode(userId, target); + showConfirmCredentialToDisableQuietMode(userId, target, callingPackage); return false; } setQuietModeEnabled(userId, false /* enableQuietMode */, target, callingPackage); @@ -1604,7 +1605,7 @@ public class UserManagerService extends IUserManager.Stub { * Show confirm credential screen to unlock user in order to turn off quiet mode. */ private void showConfirmCredentialToDisableQuietMode( - @UserIdInt int userId, @Nullable IntentSender target) { + @UserIdInt int userId, @Nullable IntentSender target, @Nullable String callingPackage) { if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) { // TODO (b/308121702) It may be brittle to rely on user states to check profile state int state; @@ -1635,6 +1636,7 @@ public class UserManagerService extends IUserManager.Stub { } callBackIntent.putExtra(Intent.EXTRA_USER_ID, userId); callBackIntent.setPackage(mContext.getPackageName()); + callBackIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackage); callBackIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final PendingIntent pendingIntent = PendingIntent.getBroadcast( mContext, 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/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java index 459e2cf7f5c0..79c9c8ec8b0b 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java +++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java @@ -28,9 +28,9 @@ import android.system.StructStat; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.ApexManager; -import com.android.server.pm.parsing.pkg.PackageImpl; import libcore.io.IoUtils; 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 b23dbee5e973..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; @@ -52,6 +53,9 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.parsing.pkg.PackageImpl; +import com.android.internal.pm.pkg.component.ComponentParseUtils; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedAttribution; import com.android.internal.pm.pkg.component.ParsedComponent; @@ -63,11 +67,12 @@ import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.pm.pkg.component.ParsedUsesPermission; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.ArrayUtils; import com.android.server.SystemConfig; import com.android.server.pm.PackageArchiver; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageStateUnserialized; @@ -75,9 +80,6 @@ import com.android.server.pm.pkg.PackageUserState; import com.android.server.pm.pkg.PackageUserStateInternal; import com.android.server.pm.pkg.PackageUserStateUtils; import com.android.server.pm.pkg.SELinuxUtil; -import com.android.server.pm.pkg.component.ComponentParseUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import java.io.File; import java.util.ArrayList; @@ -273,8 +275,8 @@ public class PackageInfoUtils { final ActivityInfo[] res = new ActivityInfo[N]; for (int i = 0; i < N; i++) { final ParsedActivity a = pkg.getActivities().get(i); - if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a, - aflags)) { + if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), + a.isEnabled(), a.isDirectBootAware(), a.getName(), aflags)) { if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals( a.getName())) { continue; @@ -293,8 +295,8 @@ public class PackageInfoUtils { final ActivityInfo[] res = new ActivityInfo[size]; for (int i = 0; i < size; i++) { final ParsedActivity a = pkg.getReceivers().get(i); - if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a, - flags)) { + if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), + a.isEnabled(), a.isDirectBootAware(), a.getName(), flags)) { res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo, userId, pkgSetting); } @@ -309,8 +311,8 @@ public class PackageInfoUtils { final ServiceInfo[] res = new ServiceInfo[size]; for (int i = 0; i < size; i++) { final ParsedService s = pkg.getServices().get(i); - if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), s, - flags)) { + if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), + s.isEnabled(), s.isDirectBootAware(), s.getName(), flags)) { res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo, userId, pkgSetting); } @@ -326,8 +328,8 @@ public class PackageInfoUtils { for (int i = 0; i < size; i++) { final ParsedProvider pr = pkg.getProviders() .get(i); - if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), pr, - flags)) { + if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), + pr.isEnabled(), pr.isDirectBootAware(), pr.getName(), flags)) { res[num++] = generateProviderInfo(pkg, pr, flags, state, applicationInfo, userId, pkgSetting); } @@ -473,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(); } @@ -923,7 +952,7 @@ public class PackageInfoUtils { | flag(pkg.isExtraLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) - | flag(AndroidPackageUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM) + | flag(AndroidPackageLegacyUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM) | flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST); return appInfoFlags(pkgWithoutStateFlags, pkgSetting); @@ -964,12 +993,12 @@ public class PackageInfoUtils { | flag(pkg.isSaveStateDisallowed(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING) - | flag(AndroidPackageUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) - | flag(AndroidPackageUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) - | flag(AndroidPackageUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM) - | flag(AndroidPackageUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR) - | flag(AndroidPackageUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT) - | flag(AndroidPackageUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM) + | flag(AndroidPackageLegacyUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) + | flag(AndroidPackageLegacyUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) + | flag(AndroidPackageLegacyUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM) + | flag(AndroidPackageLegacyUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR) + | flag(AndroidPackageLegacyUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT) + | flag(AndroidPackageLegacyUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM) | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY); Boolean resizeableActivity = pkg.getResizeableActivity(); diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java index 1c751e07bbbe..b6a08a5a546f 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java +++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java @@ -35,17 +35,19 @@ import android.util.DisplayMetrics; import android.util.Slog; import com.android.internal.compat.IPlatformCompat; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.ArrayUtils; +import com.android.server.SystemConfig; import com.android.server.pm.PackageManagerException; import com.android.server.pm.PackageManagerService; -import com.android.server.pm.parsing.pkg.PackageImpl; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import java.io.File; import java.util.List; +import java.util.Set; /** * The v2 of package parsing for use when parsing is initiated in the server and must @@ -88,6 +90,16 @@ public class PackageParser2 implements AutoCloseable { // behavior. return false; } + + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return SystemConfig.getInstance().getHiddenApiWhitelistedApps(); + } + + @Override + public Set<String> getInstallConstraintsAllowlist() { + return SystemConfig.getInstance().getInstallConstraintsAllowlist(); + } }); } @@ -221,7 +233,7 @@ public class PackageParser2 implements AutoCloseable { @NonNull String baseCodePath, @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) { return PackageImpl.forParsing(packageName, baseCodePath, codePath, manifestArray, - isCoreApp); + isCoreApp, Callback.this); } /** diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java index 61be6e1036f2..1b7c7ad94dc9 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java @@ -29,7 +29,9 @@ import android.content.pm.parsing.result.ParseTypeImpl; import android.os.incremental.IncrementalManager; import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.pm.parsing.PackageParserException; import com.android.internal.pm.parsing.pkg.AndroidPackageHidden; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.pm.pkg.component.ParsedProvider; @@ -37,7 +39,6 @@ import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.pm.pkg.parsing.ParsingPackageHidden; import com.android.internal.util.ArrayUtils; import com.android.server.SystemConfig; -import com.android.server.pm.PackageManagerException; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; @@ -134,10 +135,10 @@ public class AndroidPackageUtils { /** * Validate the dex metadata files installed for the given package. * - * @throws PackageManagerException in case of errors. + * @throws PackageParserException in case of errors. */ public static void validatePackageDexMetadata(AndroidPackage pkg) - throws PackageManagerException { + throws PackageParserException { Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values(); String packageName = pkg.getPackageName(); long versionCode = pkg.getLongVersionCode(); @@ -146,7 +147,7 @@ public class AndroidPackageUtils { final ParseResult result = DexMetadataHelper.validateDexMetadataFile( input.reset(), dexMetadata, packageName, versionCode); if (result.isError()) { - throw new PackageManagerException( + throw new PackageParserException( result.getErrorCode(), result.getErrorMessage(), result.getException()); } } @@ -314,60 +315,4 @@ public class AndroidPackageUtils { info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode(); info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor(); } - - /** - * @deprecated Use {@link PackageState#isSystem} - */ - @Deprecated - public static boolean isSystem(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isSystem(); - } - - /** - * @deprecated Use {@link PackageState#isSystemExt} - */ - @Deprecated - public static boolean isSystemExt(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isSystemExt(); - } - - /** - * @deprecated Use {@link PackageState#isPrivileged} - */ - @Deprecated - public static boolean isPrivileged(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isPrivileged(); - } - - /** - * @deprecated Use {@link PackageState#isOem} - */ - @Deprecated - public static boolean isOem(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isOem(); - } - - /** - * @deprecated Use {@link PackageState#isVendor} - */ - @Deprecated - public static boolean isVendor(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isVendor(); - } - - /** - * @deprecated Use {@link PackageState#isProduct} - */ - @Deprecated - public static boolean isProduct(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isProduct(); - } - - /** - * @deprecated Use {@link PackageState#isOdm} - */ - @Deprecated - public static boolean isOdm(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isOdm(); - } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 8bd2d94667f9..671e031b546b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -120,8 +120,11 @@ import com.android.internal.compat.IPlatformCompat; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.os.RoSystemProperties; +import com.android.internal.pm.permission.CompatibilityPermissionInfo; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.component.ParsedPermissionUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.IntPair; @@ -144,8 +147,6 @@ import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.SharedUserApi; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ParsedPermissionUtils; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; 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/SELinuxUtil.java b/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java index 6cbc1de75010..6a156415982c 100644 --- a/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java +++ b/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java @@ -16,6 +16,8 @@ package com.android.server.pm.pkg; +import com.android.internal.pm.pkg.SEInfoUtil; + /** * Utility methods that need to be used in application space. * @hide @@ -23,10 +25,10 @@ package com.android.server.pm.pkg; public final class SELinuxUtil { /** Append to existing seinfo label for instant apps @hide */ - private static final String INSTANT_APP_STR = ":ephemeralapp"; + private static final String INSTANT_APP_STR = SEInfoUtil.INSTANT_APP_STR; /** Append to existing seinfo when modifications are complete @hide */ - public static final String COMPLETE_STR = ":complete"; + public static final String COMPLETE_STR = SEInfoUtil.COMPLETE_STR; /** @hide */ public static String getSeinfoUser(PackageUserState userState) { 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/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java index 532a7f8f893f..c9da99da7902 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java @@ -45,11 +45,13 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.component.ParsedProvider; +import com.android.internal.pm.pkg.component.ParsedProviderImpl; import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.util.ArrayUtils; import com.android.server.IntentResolver; @@ -62,8 +64,6 @@ import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageStateUtils; import com.android.server.pm.pkg.PackageUserStateInternal; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ParsedProviderImpl; import com.android.server.pm.snapshot.PackageDataSnapshot; import com.android.server.utils.Snappable; import com.android.server.utils.SnapshotCache; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4e5dc1dd76fa..938ed2329ffd 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -76,7 +76,6 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; -import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -97,7 +96,6 @@ import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY; import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE; import android.accessibilityservice.AccessibilityService; -import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -124,7 +122,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; @@ -204,8 +201,6 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; -import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; -import com.android.internal.accessibility.util.AccessibilityUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; import com.android.internal.display.BrightnessUtils; @@ -385,8 +380,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { public static final String TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD = "waitForAllWindowsDrawn"; - private static final String TALKBACK_LABEL = "TalkBack"; - private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800; /** @@ -477,6 +470,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** Controller that supports enabling an AccessibilityService by holding down the volume keys */ private AccessibilityShortcutController mAccessibilityShortcutController; + private TalkbackShortcutController mTalkbackShortcutController; + boolean mSafeMode; // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key. @@ -813,7 +808,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { handleScreenShot(msg.arg1); break; case MSG_SWITCH_KEYBOARD_LAYOUT: - handleSwitchKeyboardLayout(msg.arg1, msg.arg2); + SwitchKeyboardLayoutMessageObject object = + (SwitchKeyboardLayoutMessageObject) msg.obj; + handleSwitchKeyboardLayout(object.keyEvent, object.direction, + object.focusedToken); break; case MSG_LOG_KEYBOARD_SYSTEM_EVENT: handleKeyboardSystemEvent(KeyboardLogEvent.from(msg.arg1), (KeyEvent) msg.obj); @@ -934,6 +932,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private record SwitchKeyboardLayoutMessageObject(KeyEvent keyEvent, IBinder focusedToken, + int direction) { + } + final IPersistentVrStateCallbacks mPersistentVrModeListener = new IPersistentVrStateCallbacks.Stub() { @Override @@ -1602,19 +1604,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (DEBUG_INPUT) { Slog.d(TAG, "Executing stem primary triple press action behavior."); } - - if (Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, - /* def= */ 0, UserHandle.USER_CURRENT) == 1) { - /** Toggle talkback begin */ - ComponentName componentName = getTalkbackComponent(); - if (componentName != null && toggleTalkBack(componentName)) { - /** log stem triple press telemetry if it's a talkback enabled event */ - logStemTriplePressAccessibilityTelemetry(componentName); - } - performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */ false, - /* reason = */ "Stem primary - Triple Press - Toggle Accessibility"); - /** Toggle talkback end */ + mTalkbackShortcutController.toggleTalkback(mCurrentUserId); + if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) { + performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */ + false, /* reason = */ + "Stem primary - Triple Press - Toggle Accessibility"); } break; } @@ -1640,61 +1634,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * A function that toggles talkback service - * - * @return {@code true} if talkback is enabled, {@code false} if talkback is disabled - */ - private boolean toggleTalkBack(ComponentName componentName) { - final Set<ComponentName> enabledServices = - AccessibilityUtils.getEnabledServicesFromSettings(mContext, mCurrentUserId); - - boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); - AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, - !isTalkbackAlreadyEnabled); - /** if isTalkbackAlreadyEnabled is true, then it's a disabled event so return false - * and if isTalkbackAlreadyEnabled is false, return true as it's an enabled event */ - return !isTalkbackAlreadyEnabled; - } - - /** - * A function that logs stem triple press accessibility telemetry - * If the user setup (Oobe) is not completed, set the - * WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE - * setting which will be later logged via Settings Snapshot - * else, log ACCESSIBILITY_SHORTCUT_REPORTED atom - */ - private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) { - if (!AccessibilityUtils.isUserSetupCompleted(mContext)) { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1); - } else { - AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext, componentName, - ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE, - /* serviceEnabled= */ true); - } - } - - private ComponentName getTalkbackComponent() { - AccessibilityManager accessibilityManager = mContext.getSystemService( - AccessibilityManager.class); - List<AccessibilityServiceInfo> serviceInfos = - accessibilityManager.getInstalledAccessibilityServiceList(); - - for (AccessibilityServiceInfo service : serviceInfos) { - final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo; - if (isTalkback(serviceInfo)) { - return new ComponentName(serviceInfo.packageName, serviceInfo.name); - } - } - return null; - } - - private boolean isTalkback(ServiceInfo info) { - String label = info.loadLabel(mPackageManager).toString(); - return label.equals(TALKBACK_LABEL); - } - - /** * Load most recent task (expect current task) and bring it to the front. */ void performStemPrimaryDoublePressSwitchToRecentTask() { @@ -1731,12 +1670,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TRIPLE_PRESS_PRIMARY_NOTHING: break; case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY: - if (Settings.System.getIntForUser( - mContext.getContentResolver(), - Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, - /* def= */ 0, - UserHandle.USER_CURRENT) - == 1) { + if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) { return 3; } break; @@ -2252,6 +2186,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { ButtonOverridePermissionChecker getButtonOverridePermissionChecker() { return new ButtonOverridePermissionChecker(); } + + TalkbackShortcutController getTalkbackShortcutController() { + return new TalkbackShortcutController(mContext); + } } /** {@inheritDoc} */ @@ -2515,6 +2453,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDrawnTimeout = mContext.getResources().getInteger( com.android.internal.R.integer.config_keyguardDrawnTimeout); mKeyguardDelegate = injector.getKeyguardServiceDelegate(); + mTalkbackShortcutController = injector.getTalkbackShortcutController(); initKeyCombinationRules(); initSingleKeyGestureRules(injector.getLooper()); mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker(); @@ -3709,7 +3648,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_LANGUAGE_SWITCH: if (firstDown) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; - sendSwitchKeyboardLayout(event, direction); + sendSwitchKeyboardLayout(event, focusedToken, direction); logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH); return true; } @@ -3978,7 +3917,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { + ", policyFlags=" + policyFlags); } - if (interceptUnhandledKey(event)) { + if (interceptUnhandledKey(event, focusedToken)) { return null; } @@ -4036,7 +3975,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return fallbackEvent; } - private boolean interceptUnhandledKey(KeyEvent event) { + private boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) { final int keyCode = event.getKeyCode(); final int repeatCount = event.getRepeatCount(); final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; @@ -4049,7 +3988,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK, KeyEvent.META_CTRL_ON)) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; - sendSwitchKeyboardLayout(event, direction); + sendSwitchKeyboardLayout(event, focusedToken, direction); return true; } } @@ -4105,16 +4044,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, int direction) { - mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, event.getDeviceId(), - direction).sendToTarget(); + private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, + @Nullable IBinder focusedToken, int direction) { + SwitchKeyboardLayoutMessageObject object = + new SwitchKeyboardLayoutMessageObject(event, focusedToken, direction); + mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, object).sendToTarget(); } - private void handleSwitchKeyboardLayout(int deviceId, int direction) { + private void handleSwitchKeyboardLayout(@NonNull KeyEvent event, int direction, + IBinder focusedToken) { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) { - InputMethodManagerInternal.get().switchKeyboardLayout(direction); + IBinder targetWindowToken = + mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken); + InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction, + event.getDisplayId(), targetWindowToken); } else { - mWindowManagerFuncs.switchKeyboardLayout(deviceId, direction); + mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); } } @@ -4124,7 +4069,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if ((actions & ACTION_PASS_TO_USER) != 0) { long delayMillis = interceptKeyBeforeDispatching( focusedToken, fallbackEvent, policyFlags); - if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent)) { + if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent, focusedToken)) { return true; } } diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java new file mode 100644 index 000000000000..906da2f4cdf5 --- /dev/null +++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java @@ -0,0 +1,118 @@ +/* + * 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.policy; + +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.UserHandle; +import android.provider.Settings; +import android.view.accessibility.AccessibilityManager; + +import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; +import com.android.internal.accessibility.util.AccessibilityUtils; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.List; +import java.util.Set; + +/** + * This class controls talkback shortcut related operations such as toggling, quering and + * logging. + */ +@VisibleForTesting +class TalkbackShortcutController { + private static final String TALKBACK_LABEL = "TalkBack"; + private final Context mContext; + private final PackageManager mPackageManager; + + TalkbackShortcutController(Context context) { + mContext = context; + mPackageManager = mContext.getPackageManager(); + } + + /** + * A function that toggles talkback service. + * + * @return talkback state after toggle. {@code true} if talkback is enabled, {@code false} if + * talkback is disabled + */ + boolean toggleTalkback(int userId) { + final Set<ComponentName> enabledServices = + AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId); + ComponentName componentName = getTalkbackComponent(); + boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); + + if (isTalkBackShortcutGestureEnabled()) { + isTalkbackAlreadyEnabled = !isTalkbackAlreadyEnabled; + AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, + isTalkbackAlreadyEnabled); + + // log stem triple press telemetry if it's a talkback enabled event. + if (componentName != null && isTalkbackAlreadyEnabled) { + logStemTriplePressAccessibilityTelemetry(componentName); + } + } + return isTalkbackAlreadyEnabled; + } + + private ComponentName getTalkbackComponent() { + AccessibilityManager accessibilityManager = mContext.getSystemService( + AccessibilityManager.class); + List<AccessibilityServiceInfo> serviceInfos = + accessibilityManager.getInstalledAccessibilityServiceList(); + + for (AccessibilityServiceInfo service : serviceInfos) { + final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo; + if (isTalkback(serviceInfo)) { + return new ComponentName(serviceInfo.packageName, serviceInfo.name); + } + } + return null; + } + + boolean isTalkBackShortcutGestureEnabled() { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, + /* def= */ 0, UserHandle.USER_CURRENT) == 1; + } + + /** + * A function that logs stem triple press accessibility telemetry. If the user setup (Oobe) + * is not completed, set the WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE setting which + * will be later logged via Settings Snapshot else, log ACCESSIBILITY_SHORTCUT_REPORTED atom + */ + private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) { + if (!AccessibilityUtils.isUserSetupCompleted(mContext)) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1); + return; + } + AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext, + componentName, + ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE, + /* serviceEnabled= */ true); + } + + private boolean isTalkback(ServiceInfo info) { + return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString()); + } +} diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index eac4fc00b667..9a85c42e1a10 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -1608,14 +1608,12 @@ public class TrustManagerService extends SystemService { user.name, user.id, user.flags); if (!user.supportsSwitchToByUser()) { final boolean locked; - if (user.isProfile()) { - if (mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)) { - fout.print(" (profile with separate challenge)"); - locked = isDeviceLockedInner(user.id); - } else { - fout.print(" (profile with unified challenge)"); - locked = isDeviceLockedInner(resolveProfileParent(user.id)); - } + if (mLockPatternUtils.isProfileWithUnifiedChallenge(user.id)) { + fout.print(" (profile with unified challenge)"); + locked = isDeviceLockedInner(resolveProfileParent(user.id)); + } else if (mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)) { + fout.print(" (profile with separate challenge)"); + locked = isDeviceLockedInner(user.id); } else { fout.println(" (user that cannot be switched to)"); locked = isDeviceLockedInner(user.id); 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/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java index 81e5fbd564e0..769f01c34e17 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java @@ -142,8 +142,11 @@ public class ActivityMetricsLaunchObserver { * if the launching activity is started from an existing launch sequence (trampoline) * but cannot coalesce to the existing one, e.g. to a different display. * @param name The launching activity name. + * @param temperature The temperature at which a launch sequence had started. + * @param userId The id of the user the activity is being launched for. */ - public void onActivityLaunched(long id, ComponentName name, @Temperature int temperature) { + public void onActivityLaunched(long id, ComponentName name, @Temperature int temperature, + int userId) { } /** @@ -177,13 +180,15 @@ public class ActivityMetricsLaunchObserver { * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds. * To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted} * from {@code timestampNanos}. + * @param launchMode The activity launch mode. * * @apiNote The finishing activity isn't necessarily the same as the starting activity; * in the case of a trampoline, multiple activities could've been started * and only the latest activity that was top-most during first-frame drawn * is reported here. */ - public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos) { + public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos, + int launchMode) { } /** diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 7b20529ce5e1..78f501ad9fed 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -1737,7 +1737,8 @@ class ActivityMetricsLogger { // Beginning a launch is timing sensitive and so should be observed as soon as possible. mLaunchObserver.onActivityLaunched(info.mLaunchingState.mStartUptimeNs, - info.mLastLaunchedActivity.mActivityComponent, temperature); + info.mLastLaunchedActivity.mActivityComponent, temperature, + info.mLastLaunchedActivity.mUserId); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @@ -1774,7 +1775,8 @@ class ActivityMetricsLogger { "MetricsLogger:launchObserverNotifyActivityLaunchFinished"); mLaunchObserver.onActivityLaunchFinished(info.mLaunchingState.mStartUptimeNs, - info.mLastLaunchedActivity.mActivityComponent, timestampNs); + info.mLastLaunchedActivity.mActivityComponent, timestampNs, + info.mLastLaunchedActivity.launchMode); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } 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/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 73edb4bed6ca..869bcc0f9b8f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1053,6 +1053,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mWindowManager = wm; mRootWindowContainer = wm.mRoot; mWindowOrganizerController.mTransitionController.setWindowManager(wm); + mLifecycleManager.setWindowManager(wm); mTempConfig.setToDefaults(); mTempConfig.setLocales(LocaleList.getDefault()); mConfigurationSeq = mTempConfig.seq = 1; @@ -1274,7 +1275,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { - final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions); assertPackageMatchesCallingUid(callingPackage); @@ -1315,7 +1315,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .setActivityOptions(opts) .setUserId(userId) .execute(); - } @Override diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 92665af1075b..4929df8061b2 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static android.os.Process.SYSTEM_UID; import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; @@ -49,7 +50,6 @@ import android.compat.annotation.EnabledAfter; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; -import android.os.Build; import android.os.Process; import android.os.UserHandle; import android.provider.DeviceConfig; @@ -86,7 +86,7 @@ public class BackgroundActivityStartController { private static final int NO_PROCESS_UID = -1; /** If enabled the creator will not allow BAL on its behalf by default. */ @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE) private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR = 296478951; public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED = @@ -228,6 +228,7 @@ public class BackgroundActivityStartController { private final Intent mIntent; private final WindowProcessController mCallerApp; private final WindowProcessController mRealCallerApp; + private final boolean mIsCallForResult; private BalState(int callingUid, int callingPid, final String callingPackage, int realCallingUid, int realCallingPid, @@ -247,8 +248,10 @@ public class BackgroundActivityStartController { mOriginatingPendingIntent = originatingPendingIntent; mIntent = intent; mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid); - if (originatingPendingIntent == null // not a PendingIntent - || resultRecord != null // sent for result + mIsCallForResult = resultRecord != null; + if (balRequireOptInByPendingIntentCreator() // auto-opt in introduced with this feature + && (originatingPendingIntent == null // not a PendingIntent + || mIsCallForResult) // sent for result ) { // grant BAL privileges unless explicitly opted out mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator = @@ -351,6 +354,19 @@ public class BackgroundActivityStartController { return name + "[debugOnly]"; } + /** @return valid targetSdk or <code>-1</code> */ + private int getTargetSdk(String packageName) { + if (packageName == null) { + return -1; + } + try { + PackageManager pm = mService.mContext.getPackageManager(); + return pm.getTargetSdkVersion(packageName); + } catch (Exception e) { + return -1; + } + } + private boolean hasRealCaller() { return mRealCallingUid != NO_PROCESS_UID; } @@ -368,6 +384,7 @@ public class BackgroundActivityStartController { StringBuilder sb = new StringBuilder(2048); sb.append("[callingPackage: ") .append(getDebugPackageName(mCallingPackage, mCallingUid)); + sb.append("; callingPackageTargetSdk: ").append(getTargetSdk(mCallingPackage)); sb.append("; callingUid: ").append(mCallingUid); sb.append("; callingPid: ").append(mCallingPid); sb.append("; appSwitchState: ").append(mAppSwitchState); @@ -387,10 +404,13 @@ public class BackgroundActivityStartController { .append(mBalAllowedByPiCreatorWithHardening); sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal); sb.append("; hasRealCaller: ").append(hasRealCaller()); + sb.append("; isCallForResult: ").append(mIsCallForResult); sb.append("; isPendingIntent: ").append(isPendingIntent()); if (hasRealCaller()) { sb.append("; realCallingPackage: ") .append(getDebugPackageName(mRealCallingPackage, mRealCallingUid)); + sb.append("; realCallingPackageTargetSdk: ") + .append(getTargetSdk(mRealCallingPackage)); sb.append("; realCallingUid: ").append(mRealCallingUid); sb.append("; realCallingPid: ").append(mRealCallingPid); sb.append("; realCallingUidHasAnyVisibleWindow: ") diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java index 8b282dd3e7a8..6b6388b10ce7 100644 --- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java @@ -22,7 +22,13 @@ import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; /** * Class that is able to combine multiple client lifecycle transition requests and/or callbacks, @@ -31,8 +37,18 @@ import android.os.RemoteException; * @see ClientTransaction */ class ClientLifecycleManager { - // TODO(lifecycler): Implement building transactions or global transaction. - // TODO(lifecycler): Use object pools for transactions and transaction items. + + private static final String TAG = "ClientLifecycleManager"; + + /** Mapping from client process binder to its pending transaction. */ + @VisibleForTesting + final ArrayMap<IBinder, ClientTransaction> mPendingTransactions = new ArrayMap<>(); + + private WindowManagerService mWms; + + void setWindowManager(@NonNull WindowManagerService wms) { + mWms = wms; + } /** * Schedules a transaction, which may consist of multiple callbacks and a lifecycle request. @@ -82,14 +98,24 @@ class ClientLifecycleManager { */ void scheduleTransactionItem(@NonNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem) throws RemoteException { - // TODO(b/260873529): queue the transaction items. - final ClientTransaction clientTransaction = ClientTransaction.obtain(client); - if (transactionItem.isActivityLifecycleItem()) { - clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem); + // The behavior is different depending on the flag. + // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to + // dispatch all pending transactions at once. + if (Flags.bundleClientTransactionFlag()) { + final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client); + clientTransaction.addTransactionItem(transactionItem); + + onClientTransactionItemScheduledLocked(clientTransaction); } else { - clientTransaction.addCallback(transactionItem); + // TODO(b/260873529): cleanup after launch. + final ClientTransaction clientTransaction = ClientTransaction.obtain(client); + if (transactionItem.isActivityLifecycleItem()) { + clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem); + } else { + clientTransaction.addCallback(transactionItem); + } + scheduleTransaction(clientTransaction); } - scheduleTransaction(clientTransaction); } /** @@ -100,10 +126,67 @@ class ClientLifecycleManager { void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem, @NonNull ActivityLifecycleItem lifecycleItem) throws RemoteException { - // TODO(b/260873529): replace with #scheduleTransactionItem after launch for cleanup. - final ClientTransaction clientTransaction = ClientTransaction.obtain(client); - clientTransaction.addCallback(transactionItem); - clientTransaction.setLifecycleStateRequest(lifecycleItem); + // The behavior is different depending on the flag. + // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to + // dispatch all pending transactions at once. + if (Flags.bundleClientTransactionFlag()) { + final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client); + clientTransaction.addTransactionItem(transactionItem); + clientTransaction.addTransactionItem(lifecycleItem); + + onClientTransactionItemScheduledLocked(clientTransaction); + } else { + // TODO(b/260873529): cleanup after launch. + final ClientTransaction clientTransaction = ClientTransaction.obtain(client); + clientTransaction.addCallback(transactionItem); + clientTransaction.setLifecycleStateRequest(lifecycleItem); + scheduleTransaction(clientTransaction); + } + } + + /** Executes all the pending transactions. */ + void dispatchPendingTransactions() { + final int size = mPendingTransactions.size(); + for (int i = 0; i < size; i++) { + final ClientTransaction transaction = mPendingTransactions.valueAt(i); + try { + scheduleTransaction(transaction); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to deliver transaction for " + transaction.getClient()); + } + } + mPendingTransactions.clear(); + } + + @NonNull + private ClientTransaction getOrCreatePendingTransaction(@NonNull IApplicationThread client) { + final IBinder clientBinder = client.asBinder(); + final ClientTransaction pendingTransaction = mPendingTransactions.get(clientBinder); + if (pendingTransaction != null) { + return pendingTransaction; + } + + // Create new transaction if there is no existing. + final ClientTransaction transaction = ClientTransaction.obtain(client); + mPendingTransactions.put(clientBinder, transaction); + return transaction; + } + + /** Must only be called with WM lock. */ + private void onClientTransactionItemScheduledLocked( + @NonNull ClientTransaction clientTransaction) throws RemoteException { + // TODO(b/260873529): make sure WindowSurfacePlacer#requestTraversal is called before + // ClientTransaction scheduled when needed. + + if (mWms != null && (mWms.mWindowPlacerLocked.isInLayout() + || mWms.mWindowPlacerLocked.isTraversalScheduled())) { + // The pending transactions will be dispatched when + // RootWindowContainer#performSurfacePlacementNoTrace. + return; + } + + // Dispatch the pending transaction immediately. + mPendingTransactions.remove(clientTransaction.getClient().asBinder()); scheduleTransaction(clientTransaction); } } diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 4a3d0c142e1d..32d60c5f52e6 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static com.android.input.flags.Flags.enablePointerChoreographer; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -23,6 +24,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.content.ClipData; import android.content.Context; +import android.hardware.input.InputManagerGlobal; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -31,6 +33,8 @@ import android.os.Message; import android.util.Slog; import android.view.Display; import android.view.IWindow; +import android.view.InputDevice; +import android.view.PointerIcon; import android.view.SurfaceControl; import android.view.View; import android.view.accessibility.AccessibilityManager; @@ -97,8 +101,8 @@ class DragDropController { } IBinder performDrag(int callerPid, int callerUid, IWindow window, int flags, - SurfaceControl surface, int touchSource, float touchX, float touchY, - float thumbCenterX, float thumbCenterY, ClipData data) { + SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId, + float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) { if (DEBUG_DRAG) { Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" + Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + "," @@ -208,7 +212,17 @@ class DragDropController { final SurfaceControl surfaceControl = mDragState.mSurfaceControl; mDragState.broadcastDragStartedLocked(touchX, touchY); - mDragState.overridePointerIconLocked(touchSource); + if (enablePointerChoreographer()) { + if ((touchSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { + InputManagerGlobal.getInstance().setPointerIcon( + PointerIcon.getSystemIcon( + mService.mContext, PointerIcon.TYPE_GRABBING), + mDragState.mDisplayContent.getDisplayId(), touchDeviceId, + touchPointerId, mDragState.getInputToken()); + } + } else { + mDragState.overridePointerIconLocked(touchSource); + } // remember the thumb offsets for later mDragState.mThumbOffsetX = thumbCenterX; mDragState.mThumbOffsetY = thumbCenterY; diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index a888f8467b3a..adbe3bc1d6b3 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -416,6 +416,13 @@ class DragState { return mInputInterceptor == null ? null : mInputInterceptor.mDragWindowHandle; } + IBinder getInputToken() { + if (mInputInterceptor == null || mInputInterceptor.mClientChannel == null) { + return null; + } + return mInputInterceptor.mClientChannel.getToken(); + } + /** * @param display The Display that the window being dragged is on. */ diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java index 9cbc1bdcbeeb..4c73a415d3f3 100644 --- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java +++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java @@ -84,10 +84,10 @@ class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implement } @Override - public void onActivityLaunched(long id, ComponentName name, int temperature) { + public void onActivityLaunched(long id, ComponentName name, int temperature, int userId) { mHandler.sendMessage(PooledLambda.obtainMessage( LaunchObserverRegistryImpl::handleOnActivityLaunched, - this, id, name, temperature)); + this, id, name, temperature, userId)); } @Override @@ -97,10 +97,11 @@ class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implement } @Override - public void onActivityLaunchFinished(long id, ComponentName name, long timestampNs) { + public void onActivityLaunchFinished(long id, ComponentName name, long timestampNs, + int launchMode) { mHandler.sendMessage(PooledLambda.obtainMessage( LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, - this, id, name, timestampNs)); + this, id, name, timestampNs, launchMode)); } @Override @@ -137,10 +138,10 @@ class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implement } private void handleOnActivityLaunched(long id, ComponentName name, - @Temperature int temperature) { + @Temperature int temperature, int userId) { // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. for (int i = 0; i < mList.size(); i++) { - mList.get(i).onActivityLaunched(id, name, temperature); + mList.get(i).onActivityLaunched(id, name, temperature, userId); } } @@ -151,10 +152,11 @@ class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implement } } - private void handleOnActivityLaunchFinished(long id, ComponentName name, long timestampNs) { + private void handleOnActivityLaunchFinished(long id, ComponentName name, long timestampNs, + int launchMode) { // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. for (int i = 0; i < mList.size(); i++) { - mList.get(i).onActivityLaunchFinished(id, name, timestampNs); + mList.get(i).onActivityLaunchFinished(id, name, timestampNs, launchMode); } } 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/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index d5aa2760d41a..9a75dae3569e 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -842,6 +842,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> handleResizingWindows(); clearFrameChangingWindows(); + // Called after #handleResizingWindows to include WindowStateResizeItem if any. + mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions(); + if (mWmService.mDisplayFrozen) { ProtoLog.v(WM_DEBUG_ORIENTATION, "With display frozen, orientationChangeComplete=%b", diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 56f9aa4c6361..57939bc4f348 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -340,7 +340,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource, - float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) { + int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX, + float thumbCenterY, ClipData data) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); // Validate and resolve ClipDescription data before clearing the calling identity @@ -349,7 +350,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final long ident = Binder.clearCallingIdentity(); try { return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource, - touchX, touchY, thumbCenterX, thumbCenterY, data); + touchDeviceId, touchPointerId, touchX, touchY, thumbCenterX, thumbCenterY, + data); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index fc92755c6550..5d019122d52e 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -73,6 +73,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; +import android.app.IApplicationThread; import android.app.ResultInfo; import android.app.WindowConfiguration; import android.app.servertransaction.ActivityResultItem; @@ -103,6 +104,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.server.am.HostingRecord; import com.android.server.pm.pkg.AndroidPackage; +import com.android.window.flags.Flags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -1509,23 +1511,38 @@ class TaskFragment extends WindowContainer<WindowContainer> { } try { - final ClientTransaction transaction = ClientTransaction.obtain( - next.app.getThread()); + final IApplicationThread appThread = next.app.getThread(); + final ClientTransaction transaction = Flags.bundleClientTransactionFlag() + ? null + : ClientTransaction.obtain(appThread); // Deliver all pending results. - ArrayList<ResultInfo> a = next.results; + final ArrayList<ResultInfo> a = next.results; if (a != null) { final int size = a.size(); if (!next.finishing && size > 0) { if (DEBUG_RESULTS) { Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); } - transaction.addCallback(ActivityResultItem.obtain(next.token, a)); + final ActivityResultItem activityResultItem = ActivityResultItem.obtain( + next.token, a); + if (transaction == null) { + mAtmService.getLifecycleManager().scheduleTransactionItem( + appThread, activityResultItem); + } else { + transaction.addCallback(activityResultItem); + } } } if (next.newIntents != null) { - transaction.addCallback( - NewIntentItem.obtain(next.token, next.newIntents, true /* resume */)); + final NewIntentItem newIntentItem = NewIntentItem.obtain( + next.token, next.newIntents, true /* resume */); + if (transaction == null) { + mAtmService.getLifecycleManager().scheduleTransactionItem( + appThread, newIntentItem); + } else { + transaction.addCallback(newIntentItem); + } } // Well the app will no longer be stopped. @@ -1539,10 +1556,16 @@ class TaskFragment extends WindowContainer<WindowContainer> { final int topProcessState = mAtmService.mTopProcessState; next.app.setPendingUiCleanAndForceProcessStateUpTo(topProcessState); next.abortAndClearOptionsAnimation(); - transaction.setLifecycleStateRequest( - ResumeActivityItem.obtain(next.token, topProcessState, - dc.isNextTransitionForward(), next.shouldSendCompatFakeFocus())); - mAtmService.getLifecycleManager().scheduleTransaction(transaction); + final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain( + next.token, topProcessState, dc.isNextTransitionForward(), + next.shouldSendCompatFakeFocus()); + if (transaction == null) { + mAtmService.getLifecycleManager().scheduleTransactionItem( + appThread, resumeActivityItem); + } else { + transaction.setLifecycleStateRequest(resumeActivityItem); + mAtmService.getLifecycleManager().scheduleTransaction(transaction); + } ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next); } catch (Exception e) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 808a11d4adae..516d37c0136a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -866,6 +866,11 @@ public abstract class WindowManagerInternal { public abstract ImeTargetInfo onToggleImeRequested(boolean show, @NonNull IBinder focusedToken, @NonNull IBinder requestToken, int displayId); + /** + * Returns the token to identify the target window that the IME is associated with. + */ + public abstract @Nullable IBinder getTargetWindowTokenFromInputToken(IBinder inputToken); + /** The information of input method target when IME is requested to show or hide. */ public static class ImeTargetInfo { public final String focusedWindowName; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 10dd334ed50c..2125c63eed34 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8552,6 +8552,12 @@ public class WindowManagerService extends IWindowManager.Stub fromOrientations, toOrientations); } } + + @Override + public @Nullable IBinder getTargetWindowTokenFromInputToken(IBinder inputToken) { + InputTarget inputTarget = WindowManagerService.this.getInputTargetFromToken(inputToken); + return inputTarget == null ? null : inputTarget.getWindowToken(); + } } private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy { diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index aa589024fdc6..dff718a4b7d5 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -210,6 +210,10 @@ class WindowSurfacePlacer { return mInLayout; } + boolean isTraversalScheduled() { + return mTraversalScheduled; + } + void requestTraversal() { if (mTraversalScheduled) { return; diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 3d4f866948af..2a0f1e2ede55 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -182,7 +182,7 @@ static inline IncFsSize verityTreeSizeForFile(IncFsSize fileSize) { auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE; auto hash_block_count = block_count; - for (auto i = 0; hash_block_count > 1; i++) { + while (hash_block_count > 1) { hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block; total_tree_block_count += hash_block_count; } @@ -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/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt index 02032c786563..f69f6283f968 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt @@ -24,6 +24,7 @@ import android.content.pm.SigningDetails import android.os.Build import android.util.Slog import com.android.internal.os.RoSystemProperties +import com.android.internal.pm.permission.CompatibilityPermissionInfo import com.android.modules.utils.BinaryXmlPullParser import com.android.modules.utils.BinaryXmlSerializer import com.android.server.permission.access.AccessState @@ -42,7 +43,6 @@ import com.android.server.permission.access.util.hasBits import com.android.server.permission.access.util.isInternal import com.android.server.pm.KnownPackages import com.android.server.pm.parsing.PackageInfoUtils -import com.android.server.pm.permission.CompatibilityPermissionInfo import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.pkg.PackageState diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index d62da1a02525..5b222c01d56b 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -57,11 +57,11 @@ import android.platform.test.annotations.Presubmit; import androidx.test.core.app.ApplicationProvider; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.LocalServices; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowUserManager; diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index 3011fa17d4fc..5c4716dc751e 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -25,6 +25,7 @@ import android.os.Binder import android.os.UserHandle import android.util.ArrayMap import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.parsing.pkg.ParsedPackage import com.android.internal.pm.pkg.component.ParsedActivity import com.android.server.pm.AppsFilterImpl @@ -38,7 +39,6 @@ import com.android.server.pm.Settings import com.android.server.pm.SharedLibrariesImpl import com.android.server.pm.UserManagerInternal import com.android.server.pm.UserManagerService -import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.resolution.ComponentResolver import com.android.server.pm.snapshot.PackageDataSnapshot @@ -49,6 +49,8 @@ import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import com.android.server.wm.ActivityTaskManagerInternal import com.google.common.truth.Truth.assertThat +import java.io.File +import java.util.UUID import org.junit.After import org.junit.Before import org.junit.BeforeClass @@ -61,8 +63,6 @@ import org.mockito.Mockito.doReturn import org.mockito.Mockito.intThat import org.mockito.Mockito.same import org.testng.Assert.assertThrows -import java.io.File -import java.util.UUID @RunWith(Parameterized::class) class PackageManagerComponentLabelIconOverrideTest { diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java index 3461bb6b2c55..7277fd79fdd5 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java @@ -49,20 +49,20 @@ import android.util.SparseArray; import androidx.annotation.NonNull; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedActivityImpl; +import com.android.internal.pm.pkg.component.ParsedComponentImpl; +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl; +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl; import com.android.internal.pm.pkg.component.ParsedPermission; +import com.android.internal.pm.pkg.component.ParsedPermissionImpl; +import com.android.internal.pm.pkg.component.ParsedProviderImpl; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.server.om.OverlayReferenceMapper; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.component.ParsedActivityImpl; -import com.android.server.pm.pkg.component.ParsedComponentImpl; -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl; -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl; -import com.android.server.pm.pkg.component.ParsedPermissionImpl; -import com.android.server.pm.pkg.component.ParsedProviderImpl; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.server.utils.WatchableTester; import org.junit.Before; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java index f0d389be7cb6..b1c3e94c2138 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java @@ -32,11 +32,11 @@ import android.content.pm.ApplicationInfo; import android.os.Build; import android.platform.test.annotations.Presubmit; +import com.android.internal.pm.parsing.pkg.PackageImpl; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.parsing.PackageInfoUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.PackageStateUnserialized; import com.android.server.pm.pkg.PackageUserStateImpl; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import org.junit.After; import org.junit.Before; 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 f07e820bf8b3..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; @@ -63,10 +67,11 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +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.pkg.PackageImpl; +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/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java index 9c48af8ecd01..285c059e973a 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java @@ -73,7 +73,7 @@ import androidx.test.filters.Suppress; import com.android.compatibility.common.util.CddTest; import com.android.internal.content.InstallLocationUtils; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; +import com.android.server.pm.parsing.ParsingUtils; import com.android.server.pm.test.service.server.R; import dalvik.system.VMRuntime; @@ -346,7 +346,7 @@ public class PackageManagerTests extends AndroidTestCase { private ParsedPackage parsePackage(Uri packageURI) { final String archiveFilePath = packageURI.getPath(); - ParseResult<ParsedPackage> result = ParsingPackageUtils.parseDefaultOneTime( + ParseResult<ParsedPackage> result = ParsingUtils.parseDefaultOneTime( new File(archiveFilePath), 0 /*flags*/, Collections.emptyList(), false /*collectCertificates*/); if (result.isError()) { diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java index a62cd4fc5fc2..03e45a27390f 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java @@ -15,7 +15,7 @@ */ package com.android.server.pm; -import static com.android.server.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS; +import static com.android.internal.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -58,17 +58,28 @@ import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.permission.CompatibilityPermissionInfo; import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedActivityImpl; import com.android.internal.pm.pkg.component.ParsedApexSystemService; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedInstrumentation; +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl; import com.android.internal.pm.pkg.component.ParsedIntentInfo; +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl; +import com.android.internal.pm.pkg.component.ParsedPermissionImpl; +import com.android.internal.pm.pkg.component.ParsedPermissionUtils; import com.android.internal.pm.pkg.component.ParsedProvider; +import com.android.internal.pm.pkg.component.ParsedProviderImpl; import com.android.internal.pm.pkg.component.ParsedService; +import com.android.internal.pm.pkg.component.ParsedServiceImpl; import com.android.internal.pm.pkg.component.ParsedUsesPermission; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.internal.util.ArrayUtils; import com.android.server.pm.parsing.PackageCacher; @@ -76,19 +87,8 @@ import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.TestPackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; -import com.android.server.pm.permission.CompatibilityPermissionInfo; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageUserStateInternal; -import com.android.server.pm.pkg.component.ParsedActivityImpl; -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl; -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl; -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl; -import com.android.server.pm.pkg.component.ParsedPermissionImpl; -import com.android.server.pm.pkg.component.ParsedPermissionUtils; -import com.android.server.pm.pkg.component.ParsedProviderImpl; -import com.android.server.pm.pkg.component.ParsedServiceImpl; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; import org.junit.Before; import org.junit.Rule; @@ -725,6 +725,16 @@ public class PackageParserTest { public boolean hasFeature(String feature) { return false; } + + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return new ArraySet<>(); + } + + @Override + public Set<String> getInstallConstraintsAllowlist() { + return new ArraySet<>(); + } }); if (cacheDir != null) { setCacheDir(cacheDir); 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/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java index 6202908cdfbf..c1271bb0ee36 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java @@ -50,13 +50,13 @@ import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.util.Pair; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.server.compat.PlatformCompat; import com.android.server.pm.parsing.PackageInfoUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import org.hamcrest.BaseMatcher; @@ -376,7 +376,7 @@ public class ScanTests { // Create the ParsedPackage for the apex final ParsedPackage basicPackage = ((ParsedPackage) new PackageImpl(DUMMY_PACKAGE_NAME, codePath, codePath, - mock(TypedArray.class), false) + mock(TypedArray.class), false, null) .setVolumeUuid(UUID_ONE.toString()) .hideAsParsed()) .setVersionCodeMajor(1) @@ -595,7 +595,7 @@ public class ScanTests { // TODO(b/135203078): Make this use PackageImpl.forParsing and separate the steps return (ParsingPackage) ((ParsedPackage) new PackageImpl(packageName, "/data/tmp/randompath/base.apk", createCodePath(packageName), - mock(TypedArray.class), false) + mock(TypedArray.class), false, null) .setVolumeUuid(UUID_ONE.toString()) .addUsesStaticLibrary("some.static.library", 234L, new String[]{"testCert1"}) .addUsesStaticLibrary("some.other.static.library", 456L, new String[]{"testCert2"}) diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java index b102ab4f7e3b..b63950c10a18 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java @@ -39,14 +39,14 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ParsedActivityUtils; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedPermission; +import com.android.internal.pm.pkg.component.ParsedPermissionUtils; import com.android.internal.util.ArrayUtils; import com.android.server.pm.PackageManagerException; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.component.ParsedActivityUtils; -import com.android.server.pm.pkg.component.ParsedPermissionUtils; import com.android.server.pm.test.service.server.R; import com.google.common.truth.Expect; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt index 67b91d2646d9..c435b94fcacd 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt @@ -22,7 +22,6 @@ import android.content.pm.parsing.result.ParseResult import android.platform.test.annotations.Presubmit import androidx.test.InstrumentationRegistry import com.android.internal.pm.parsing.pkg.ParsedPackage -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.test.service.server.R import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -121,7 +120,7 @@ class PackageParsingDeferErrorTest { input.copyTo(output) } } - return ParsingPackageUtils.parseDefaultOneTime(file, 0 /*flags*/, emptyList(), + return ParsingUtils.parseDefaultOneTime(file, 0 /*flags*/, emptyList(), false /*collectCertificates*/) } } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java new file mode 100644 index 000000000000..a9eac95dfd87 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java @@ -0,0 +1,81 @@ +/* + * 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.pm.parsing; + +import android.annotation.NonNull; +import android.content.pm.parsing.result.ParseInput; +import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; +import android.content.res.TypedArray; +import android.permission.PermissionManager; + +import com.android.internal.pm.parsing.pkg.PackageImpl; +import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.server.SystemConfig; + +import java.io.File; +import java.util.List; +import java.util.Set; + +/** @hide **/ +public class ParsingUtils { + + /** + * @see ParsingPackageUtils#parseDefault(ParseInput, File, int, List, boolean, + * ParsingPackageUtils.Callback) + */ + @NonNull + public static ParseResult<ParsedPackage> parseDefaultOneTime(File file, + @ParsingPackageUtils.ParseFlags int parseFlags, + @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, + boolean collectCertificates) { + ParseInput input = ParseTypeImpl.forDefaultParsing().reset(); + return ParsingPackageUtils.parseDefault(input, file, parseFlags, splitPermissions, collectCertificates, + new ParsingPackageUtils.Callback() { + @Override + public boolean hasFeature(String feature) { + // Assume the device doesn't support anything. This will affect permission + // parsing and will force <uses-permission/> declarations to include all + // requiredNotFeature permissions and exclude all requiredFeature + // permissions. This mirrors the old behavior. + return false; + } + + @Override + public ParsingPackage startParsingPackage( + @NonNull String packageName, + @NonNull String baseApkPath, + @NonNull String path, + @NonNull TypedArray manifestArray, boolean isCoreApp) { + return PackageImpl.forParsing(packageName, baseApkPath, path, manifestArray, + isCoreApp, this); + } + + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return SystemConfig.getInstance().getHiddenApiWhitelistedApps(); + } + + @Override + public Set<String> getInstallConstraintsAllowlist() { + return SystemConfig.getInstance().getInstallConstraintsAllowlist(); + } + }); + } +} diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt index 1f57b6c9f95f..98af63c65e90 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt @@ -17,15 +17,15 @@ package com.android.server.pm.parsing import android.content.pm.PackageManager -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import android.platform.test.annotations.Postsubmit +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.PackageManagerException import com.android.server.pm.PackageManagerService import com.android.server.pm.PackageManagerServiceUtils +import java.io.File import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import java.io.File /** * This test parses all the system APKs on the device image to ensure that they succeed. diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java index 6cd71237efa2..9517e49c4a73 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java @@ -24,8 +24,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java index 27fd781584f2..01fad8f57d16 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java index b13d6de55cf6..b1f26c253043 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java @@ -23,8 +23,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java index fa69f844c33a..349763ae18b3 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java @@ -24,9 +24,9 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java index 856013a96017..71bdacbce9f7 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java @@ -22,9 +22,9 @@ import android.util.ArrayMap; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.SystemConfig; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Before; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java index ae5ea21aec4e..6aa0c2db4d39 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java index e126ffcab5ff..44098d0be2b2 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java @@ -23,8 +23,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java index d0b0cf894340..9d5ce8a66395 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java @@ -28,10 +28,10 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Assume; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java index c141c0337540..bffeb7255b31 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java @@ -23,9 +23,9 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java index a58604b81e06..b114cd375050 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java @@ -23,9 +23,9 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt index 09b66c11d200..ef9c62fd3795 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt @@ -30,21 +30,21 @@ import android.util.ArraySet import android.util.SparseArray import android.util.SparseIntArray import com.android.internal.R -import com.android.server.pm.parsing.pkg.AndroidPackageUtils -import com.android.server.pm.parsing.pkg.PackageImpl +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils +import com.android.internal.pm.parsing.pkg.PackageImpl +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedApexSystemServiceImpl +import com.android.internal.pm.pkg.component.ParsedAttributionImpl +import com.android.internal.pm.pkg.component.ParsedComponentImpl +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl +import com.android.internal.pm.pkg.component.ParsedPermissionImpl +import com.android.internal.pm.pkg.component.ParsedProcessImpl +import com.android.internal.pm.pkg.component.ParsedProviderImpl +import com.android.internal.pm.pkg.component.ParsedServiceImpl +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl -import com.android.server.pm.pkg.component.ParsedAttributionImpl -import com.android.server.pm.pkg.component.ParsedComponentImpl -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl -import com.android.server.pm.pkg.component.ParsedPermissionImpl -import com.android.server.pm.pkg.component.ParsedProcessImpl -import com.android.server.pm.pkg.component.ParsedProviderImpl -import com.android.server.pm.pkg.component.ParsedServiceImpl -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import java.security.KeyPairGenerator @@ -534,34 +534,34 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag } ), getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")), - getSetByValue({ AndroidPackageUtils.isOdm(it) }, "isOdm", PackageImpl::setOdm, true), - getSetByValue({ AndroidPackageUtils.isOem(it) }, "isOem", PackageImpl::setOem, true), + getSetByValue({ AndroidPackageLegacyUtils.isOdm(it) }, "isOdm", PackageImpl::setOdm, true), + getSetByValue({ AndroidPackageLegacyUtils.isOem(it) }, "isOem", PackageImpl::setOem, true), getSetByValue( - { AndroidPackageUtils.isPrivileged(it) }, + { AndroidPackageLegacyUtils.isPrivileged(it) }, "isPrivileged", PackageImpl::setPrivileged, true ), getSetByValue( - { AndroidPackageUtils.isProduct(it) }, + { AndroidPackageLegacyUtils.isProduct(it) }, "isProduct", PackageImpl::setProduct, true ), getSetByValue( - { AndroidPackageUtils.isVendor(it) }, + { AndroidPackageLegacyUtils.isVendor(it) }, "isVendor", PackageImpl::setVendor, true ), getSetByValue( - { AndroidPackageUtils.isSystem(it) }, + { AndroidPackageLegacyUtils.isSystem(it) }, "isSystem", PackageImpl::setSystem, true ), getSetByValue( - { AndroidPackageUtils.isSystemExt(it) }, + { AndroidPackageLegacyUtils.isSystemExt(it) }, "isSystemExt", PackageImpl::setSystemExt, true @@ -593,7 +593,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag ) ) { "" } }, - true + true, null ) .asSplit( arrayOf("testSplitNameZero", "testSplitNameOne"), diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt index 26468544e8d6..2c8b1cd884f1 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt @@ -18,7 +18,7 @@ package com.android.server.pm.test.parsing.parcelling import android.content.pm.ActivityInfo import com.android.internal.pm.pkg.component.ParsedActivity -import com.android.server.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedActivityImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt index 52d5b3bccb72..ad5374672d22 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedAttribution -import com.android.server.pm.pkg.component.ParsedAttributionImpl +import com.android.internal.pm.pkg.component.ParsedAttributionImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt index af0c0de2db15..3ac48536b39c 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt @@ -18,8 +18,8 @@ package com.android.server.pm.test.parsing.parcelling import android.content.pm.PackageManager import com.android.internal.pm.pkg.component.ParsedComponent -import com.android.server.pm.pkg.component.ParsedComponentImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl +import com.android.internal.pm.pkg.component.ParsedComponentImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import android.os.Bundle import android.os.Parcelable import kotlin.contracts.ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt index dc0f194f10cc..2bd4f617dcb5 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedInstrumentation -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt index 5224f23d38d1..af385e202a9e 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedIntentInfo -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import android.os.Parcelable import android.os.PatternMatcher import kotlin.contracts.ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt index dfff6025e2eb..061e39ddf6df 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedMainComponent -import com.android.server.pm.pkg.component.ParsedMainComponentImpl +import com.android.internal.pm.pkg.component.ParsedMainComponentImpl import android.os.Parcelable import java.util.Arrays import kotlin.contracts.ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt index ccbf558734d3..3a6418824dfa 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedPermissionGroup -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt index 2814783b6849..551f16d2a3bb 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt @@ -18,8 +18,8 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedPermission import com.android.internal.pm.pkg.component.ParsedPermissionGroup -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl -import com.android.server.pm.pkg.component.ParsedPermissionImpl +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl +import com.android.internal.pm.pkg.component.ParsedPermissionImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt index 2e9604696acb..93bdeaeb90e3 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt @@ -16,9 +16,9 @@ package com.android.server.pm.test.parsing.parcelling -import com.android.internal.pm.pkg.component.ParsedProcess -import com.android.server.pm.pkg.component.ParsedProcessImpl import android.util.ArrayMap +import com.android.internal.pm.pkg.component.ParsedProcess +import com.android.internal.pm.pkg.component.ParsedProcessImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt index 290dbd6277b6..1e844705fe3c 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt @@ -17,9 +17,9 @@ package com.android.server.pm.test.parsing.parcelling import android.content.pm.PathPermission -import com.android.internal.pm.pkg.component.ParsedProvider -import com.android.server.pm.pkg.component.ParsedProviderImpl import android.os.PatternMatcher +import com.android.internal.pm.pkg.component.ParsedProvider +import com.android.internal.pm.pkg.component.ParsedProviderImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt index 3ae7e9220cc6..79d5a4f7030a 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedService -import com.android.server.pm.pkg.component.ParsedServiceImpl +import com.android.internal.pm.pkg.component.ParsedServiceImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt index 67dfc6d4f7ef..d0ad09be982b 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedUsesPermission -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt index 1da3a2234ffc..b21c34905bad 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt @@ -17,37 +17,34 @@ package com.android.server.pm.test.pkg import android.content.Intent -import android.content.pm.overlay.OverlayPaths import android.content.pm.PackageManager import android.content.pm.PathPermission import android.content.pm.SharedLibraryInfo import android.content.pm.VersionedPackage +import android.content.pm.overlay.OverlayPaths import android.os.PatternMatcher import android.util.ArraySet +import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.pkg.component.ParsedActivity +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedComponentImpl import com.android.internal.pm.pkg.component.ParsedInstrumentation +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.internal.pm.pkg.component.ParsedPermission import com.android.internal.pm.pkg.component.ParsedPermissionGroup +import com.android.internal.pm.pkg.component.ParsedPermissionImpl import com.android.internal.pm.pkg.component.ParsedProcess +import com.android.internal.pm.pkg.component.ParsedProcessImpl import com.android.internal.pm.pkg.component.ParsedProvider +import com.android.internal.pm.pkg.component.ParsedProviderImpl import com.android.internal.pm.pkg.component.ParsedService import com.android.server.pm.PackageSetting import com.android.server.pm.PackageSettingBuilder -import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.pkg.PackageState import com.android.server.pm.pkg.PackageUserState -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedComponentImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl -import com.android.server.pm.pkg.component.ParsedPermissionImpl -import com.android.server.pm.pkg.component.ParsedProcessImpl -import com.android.server.pm.pkg.component.ParsedProviderImpl import com.android.server.pm.test.parsing.parcelling.AndroidPackageTest import com.google.common.truth.Expect -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TemporaryFolder import kotlin.contracts.ExperimentalContracts import kotlin.reflect.KClass import kotlin.reflect.KFunction @@ -55,6 +52,9 @@ import kotlin.reflect.KType import kotlin.reflect.full.isSubtypeOf import kotlin.reflect.full.memberFunctions import kotlin.reflect.full.starProjectedType +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder class PackageStateTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt index 9341e9d96335..5e73d198f528 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt @@ -22,11 +22,11 @@ import android.os.Build import android.os.PatternMatcher import android.util.ArraySet import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.SystemConfig import com.android.server.compat.PlatformCompat import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationCollector import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt index a737b9097b53..d307608e0be2 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt @@ -29,17 +29,22 @@ import android.util.IndentingPrintWriter import android.util.SparseArray import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.Computer import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationEnforcer import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever +import java.util.UUID +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger +import kotlin.test.assertFailsWith +import kotlin.test.fail import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -51,11 +56,6 @@ import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.verifyNoMoreInteractions -import java.util.UUID -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicInteger -import kotlin.test.assertFailsWith -import kotlin.test.fail @RunWith(Parameterized::class) class DomainVerificationEnforcerTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt index f38df22af630..5edf30a33def 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt @@ -30,24 +30,24 @@ import android.os.Process import android.util.ArraySet import android.util.SparseArray import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationManagerStub import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat +import java.util.UUID +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.test.assertFailsWith import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.doReturn -import java.util.UUID -import java.util.concurrent.atomic.AtomicBoolean -import kotlin.test.assertFailsWith class DomainVerificationManagerApiTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt index 874e0d2bbc9a..85f012534113 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt @@ -37,27 +37,27 @@ import android.util.ArraySet import android.util.SparseArray import android.util.Xml import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.Computer import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.testutils.mock import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.spy import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.security.PublicKey +import java.util.UUID import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.doReturn -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.security.PublicKey -import java.util.UUID class DomainVerificationPackageTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt index 3207e6c2a411..a5c4f6cc0289 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt @@ -25,15 +25,16 @@ import android.os.Process import android.util.ArraySet import android.util.SparseArray import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever +import java.util.UUID import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -44,7 +45,6 @@ import org.mockito.Mockito.anyString import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.verify -import java.util.UUID @RunWith(Parameterized::class) class DomainVerificationSettingsMutationTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt index a90b7d5ec7da..ae570a3a0ee2 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt @@ -27,14 +27,15 @@ import android.os.Process import android.util.ArraySet import android.util.SparseArray import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat +import java.util.UUID import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt @@ -42,8 +43,6 @@ import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.doReturn -import java.util.UUID - class DomainVerificationUserStateOverrideTest { companion object { 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/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 76d4d55f4d5d..9739e4b46063 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -2423,6 +2423,14 @@ public class GameManagerServiceTests { } })); + when(mSysPropsMock.getInt( + ArgumentMatchers.eq(PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE), + anyInt())).thenReturn(60); + when(mSysPropsMock.getBoolean( + ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED), + ArgumentMatchers.eq(true))).thenReturn(true); + gameManagerService.onBootCompleted(); + // Set up a game in the foreground. String[] packages = {mPackageName}; when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages); @@ -2430,12 +2438,6 @@ public class GameManagerServiceTests { DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0); // Toggle game default frame rate on. - when(mSysPropsMock.getInt( - ArgumentMatchers.eq(PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE), - anyInt())).thenReturn(60); - when(mSysPropsMock.getBoolean( - ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED), - ArgumentMatchers.eq(true))).thenReturn(true); gameManagerService.toggleGameDefaultFrameRate(true); // Verify that: diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java index 23886a17c890..00fe3d92413c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java @@ -25,6 +25,7 @@ import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BACKGROUND import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -167,8 +168,12 @@ public class BackgroundJobsControllerTest { } private void setStoppedState(int uid, String pkgName, boolean stopped) { - doReturn(stopped).when(mPackageManagerInternal).isPackageStopped(pkgName, uid); - sendPackageStoppedBroadcast(uid, pkgName, stopped); + try { + doReturn(stopped).when(mPackageManagerInternal).isPackageStopped(pkgName, uid); + sendPackageStoppedBroadcast(uid, pkgName, stopped); + } catch (PackageManager.NameNotFoundException e) { + fail("Unable to set stopped state for unknown package: " + pkgName); + } } private void sendPackageStoppedBroadcast(int uid, String pkgName, boolean stopped) { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index de8b3080907c..c2b52b4ee9c8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -45,14 +45,15 @@ import android.os.Environment; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.parsing.PackageParser2; -import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import org.junit.Before; import org.junit.Rule; @@ -67,6 +68,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Objects; +import java.util.Set; @SmallTest @Presubmit @@ -106,6 +108,18 @@ public class ApexManagerTest { public boolean hasFeature(String feature) { return true; } + + @androidx.annotation.NonNull + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return new ArraySet<>(); + } + + @androidx.annotation.NonNull + @Override + public Set<String> getInstallConstraintsAllowlist() { + return new ArraySet<>(); + } }); mMockSystem.system().stageNominalSystemState(); @@ -385,7 +399,7 @@ public class ApexManagerTest { findFactory(results, "test.apex.rebootless").apexInfo); assertThat(factoryPkg.getBaseApkPath()).isEqualTo(activeApexInfo.modulePath); assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1); - assertThat(AndroidPackageUtils.isSystem(factoryPkg)).isTrue(); + assertThat(AndroidPackageLegacyUtils.isSystem(factoryPkg)).isTrue(); } @Test @@ -416,7 +430,7 @@ public class ApexManagerTest { findFactory(results, "test.apex.rebootless").apexInfo); assertThat(factoryPkg.getBaseApkPath()).isEqualTo(factoryApexInfo.modulePath); assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1); - assertThat(AndroidPackageUtils.isSystem(factoryPkg)).isTrue(); + assertThat(AndroidPackageLegacyUtils.isSystem(factoryPkg)).isTrue(); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 28bd98781ced..7b29e2a4159d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -56,8 +56,10 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.spy import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder import com.android.internal.R +import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.parsing.pkg.ParsedPackage import com.android.internal.pm.pkg.parsing.ParsingPackage +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.server.LocalManagerRegistry import com.android.server.LocalServices import com.android.server.LockGuard @@ -68,10 +70,8 @@ import com.android.server.extendedtestutils.wheneverStatic import com.android.server.pm.dex.DexManager import com.android.server.pm.dex.DynamicCodeLogger import com.android.server.pm.parsing.PackageParser2 -import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.resolution.ComponentResolver import com.android.server.pm.snapshot.PackageDataSnapshot import com.android.server.pm.verify.domain.DomainVerificationManagerInternal @@ -81,14 +81,6 @@ import com.android.server.testutils.mock import com.android.server.testutils.nullable import com.android.server.testutils.whenever import com.android.server.utils.WatchedArrayMap -import libcore.util.HexEncoding -import org.junit.Assert -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import org.mockito.AdditionalMatchers.or -import org.mockito.Mockito -import org.mockito.quality.Strictness import java.io.File import java.io.IOException import java.nio.file.Files @@ -97,6 +89,14 @@ import java.security.cert.CertificateException import java.util.Arrays import java.util.Random import java.util.concurrent.FutureTask +import libcore.util.HexEncoding +import org.junit.Assert +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import org.mockito.AdditionalMatchers.or +import org.mockito.Mockito +import org.mockito.quality.Strictness /** * A utility for mocking behavior of the system and dependencies when testing PackageManagerService diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java index 2332988cc832..e5ecdc478df7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java @@ -62,6 +62,7 @@ import android.os.ParcelableException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.text.TextUtils; +import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -174,6 +175,7 @@ public class PackageArchiverTest { mUserState = new PackageUserStateImpl().setInstalled(true); mPackageSetting.setUserState(mUserId, mUserState); when(mPackageState.getUserStateOrDefault(eq(mUserId))).thenReturn(mUserState); + when(mPackageState.getUserStates()).thenReturn(new SparseArray<>()); when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); when(mContext.getSystemService(AppOpsManager.class)).thenReturn( @@ -343,7 +345,7 @@ public class PackageArchiverTest { @Test public void archiveApp_appOptedOutOfArchiving() { - when(mAppOpsManager.checkOp( + when(mAppOpsManager.checkOpNoThrow( eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(PACKAGE))).thenReturn(MODE_IGNORED); @@ -430,7 +432,7 @@ public class PackageArchiverTest { @Test public void isAppArchivable_appOptedOutOfArchiving() throws PackageManager.NameNotFoundException { - when(mAppOpsManager.checkOp( + when(mAppOpsManager.checkOpNoThrow( eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(PACKAGE))).thenReturn(MODE_IGNORED); @@ -551,22 +553,12 @@ public class PackageArchiverTest { when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn( null); - Exception e = assertThrows( - ParcelableException.class, - () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)); - assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); - assertThat(e.getCause()).hasMessageThat().isEqualTo( - String.format("Package %s not found.", PACKAGE)); + assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); } @Test public void getArchivedAppIcon_notArchived() { - Exception e = assertThrows( - ParcelableException.class, - () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)); - assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); - assertThat(e.getCause()).hasMessageThat().isEqualTo( - String.format("Package %s is not currently archived.", PACKAGE)); + assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt index a6ba5d4c3032..7b80aea80035 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt @@ -36,6 +36,7 @@ import org.mockito.MockitoAnnotations open class PackageHelperTestBase { companion object { + const val PLATFORM_PACKAGE_NAME = "android" const val TEST_PACKAGE_1 = "com.android.test.package1" const val TEST_PACKAGE_2 = "com.android.test.package2" const val DEVICE_OWNER_PACKAGE = "com.android.test.owner" diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt index e685c3fa9fa4..944b1aae7bda 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt @@ -21,17 +21,17 @@ import android.content.pm.PackageManager import android.content.pm.SharedLibraryInfo import android.content.pm.VersionedPackage import android.os.Build -import android.os.storage.StorageManager import android.os.UserHandle +import android.os.storage.StorageManager import android.util.ArrayMap import android.util.PackageUtils +import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.parsing.pkg.ParsedPackage import com.android.server.SystemConfig.SharedLibraryEntry import com.android.server.compat.PlatformCompat import com.android.server.extendedtestutils.wheneverStatic import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.testutils.any import com.android.server.testutils.eq import com.android.server.testutils.mock 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 147303363200..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,79 @@ 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 + fun getSuspendingPackagePrecedence() { + val launcherExtras = PersistableBundle() + launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2) + val targetPackages = arrayOf(TEST_PACKAGE_2) + // Suspend. + var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), + targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, + 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(doUserPackage) + + // Suspend by system. + failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), + targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, + 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(platformUserPackage) + + // QAS by package1. + failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), + targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, + 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(testUserPackage1) + + // Un-QAS by package1. + suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), + targetPackages, { suspendingPackage -> suspendingPackage == testUserPackage1 }, + TEST_USER_ID) + testHandler.flush() + + assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(platformUserPackage) + + // Un-suspend by system. + suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), + 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(doUserPackage) + + // Unsuspend. + suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), + targetPackages, { suspendingPackage -> suspendingPackage == doUserPackage }, + TEST_USER_ID) + testHandler.flush() + + assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull() } @Test @@ -288,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/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index a2a8424881d4..0973d46283ed 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -23,6 +23,7 @@ import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; import static com.android.server.hdmi.Constants.ADDR_RECORDER_1; import static com.android.server.hdmi.Constants.ADDR_TV; +import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.google.common.truth.Truth.assertThat; @@ -1712,13 +1713,14 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); + assertThat(mHdmiControlService.getLocalActiveSource()).isEqualTo( + new ActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS)); mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); mTestLooper.dispatchAll(); @@ -1728,8 +1730,64 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(activeSourceFromTv); + assertThat(mHdmiControlService.getLocalActiveSource()).isEqualTo( + new ActiveSource(mTvLogicalAddress, mTvPhysicalAddress)); + } + + @Test + public void requestActiveSourceActionComplete_validLocalActiveSource_doNotSendActiveSource() { + HdmiCecMessage requestActiveSource = + HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); + HdmiCecMessage activeSourceFromTv = + HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); + mHdmiControlService.setActiveSource(mTvLogicalAddress, mTvPhysicalAddress, + "HdmiCecLocalDeviceTvTest"); + mNativeWrapper.clearResultMessages(); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); + } + + @Test + public void onAddressAllocated_startRequestActiveSourceAction_cancelOnDeviceSelect() { + HdmiCecMessage requestActiveSource = + HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); + HdmiCecMessage activeSourceFromTv = + HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + HdmiDeviceInfo playbackDevice = HdmiDeviceInfo.cecDeviceBuilder() + .setLogicalAddress(ADDR_PLAYBACK_1) + .setPhysicalAddress(0x1000) + .setPortId(PORT_1) + .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK) + .setVendorId(0x1234) + .setDisplayName("Playback 1") + .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON) + .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B) + .build(); + mHdmiControlService.getHdmiCecNetwork().addCecDevice(playbackDevice); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); + mNativeWrapper.clearResultMessages(); + mHdmiCecLocalDeviceTv.deviceSelect(playbackDevice.getId(), null); + mTestLooper.dispatchAll(); + + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); + mTestLooper.dispatchAll(); + + // RequestActiveSourceAction should be cancelled and TV will not broadcast <Active Source>. + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); } + @Test public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() { mHdmiControlService.getHdmiCecNetwork().clearDeviceList(); @@ -1737,7 +1795,7 @@ public class HdmiCecLocalDeviceTvTest { .isEmpty(); HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( - ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK); + ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK); HdmiCecMessage giveOsdName = HdmiCecMessageBuilder.buildGiveOsdNameCommand( ADDR_TV, ADDR_PLAYBACK_2); mNativeWrapper.onCecMessage(reportPhysicalAddress); 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/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index 18961c0feef9..ee076c6bcf4b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -140,9 +140,9 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override - public ManagedProfilePasswordCache getManagedProfilePasswordCache( + public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache( java.security.KeyStore ks) { - return mock(ManagedProfilePasswordCache.class); + return mock(UnifiedProfilePasswordCache.class); } @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java index dd687fd4286c..86a1358f371c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java @@ -27,9 +27,9 @@ import static org.mockito.Mockito.when; import android.os.Build; import android.platform.test.annotations.Presubmit; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.compat.PlatformCompat; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.PackageState; import org.junit.Test; diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java index 8464969cd0f3..ee93bc1e3562 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java @@ -53,10 +53,10 @@ import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiDevice; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.LocalServices; import com.android.server.SystemConfig; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.After; diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java index 0f87202f8939..587f5fa9c623 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java @@ -29,9 +29,9 @@ import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import dalvik.system.DelegateLastClassLoader; diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt index ee23a00f27d7..9b4ca2a86f2c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt @@ -21,17 +21,17 @@ import android.os.Environment import android.os.SystemProperties.PROP_VALUE_MAX import android.platform.test.annotations.Postsubmit import com.android.internal.R +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.PackageManagerService -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import com.google.common.truth.Truth.assertThat +import java.io.ByteArrayInputStream +import java.io.File import org.junit.Assert.assertEquals import org.junit.Assert.assertThrows import org.junit.Assert.fail import org.junit.Test import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserFactory -import java.io.ByteArrayInputStream -import java.io.File @Postsubmit class AndroidPackageParsingValidationTest { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt index 2332817911f7..c44f583a93ef 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt @@ -17,6 +17,7 @@ package com.android.server.pm.parsing import android.content.pm.ApplicationInfo +import android.util.ArraySet import java.io.File class TestPackageParser2(var cacheDir: File? = null) : PackageParser2( @@ -33,4 +34,7 @@ class TestPackageParser2(var cacheDir: File? = null) : PackageParser2( // behavior. return false } + + override fun getHiddenApiWhitelistedApps() = ArraySet<String>() + override fun getInstallConstraintsAllowlist() = ArraySet<String>() }) diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index 5ba4851270fd..759b204516c6 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -32,8 +32,9 @@ import android.content.rollback.PackageRollbackInfo; import android.util.SparseArray; import android.util.SparseIntArray; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.server.pm.PackageList; -import com.android.server.pm.parsing.pkg.PackageImpl; + import com.google.common.collect.Range; import org.junit.Before; @@ -415,7 +416,7 @@ public class RollbackUnitTest { private void addPkgWithMinExtVersions(String pkg, int[][] minExtVersions) { mPackages.add(pkg); - PackageImpl pkgImpl = new PackageImpl(pkg, "baseCodePath", "codePath", null, false); + PackageImpl pkgImpl = new PackageImpl(pkg, "baseCodePath", "codePath", null, false, null); pkgImpl.setMinExtensionVersions(sparseArrayFrom(minExtVersions)); when(mMockPmi.getPackage(pkg)).thenReturn(pkgImpl); diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 6335b3bfade2..4e1c72af2727 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -46,6 +46,7 @@ android_test { "testng", "flag-junit", "notification_flags_lib", + "platform-test-rules", ], libs: [ 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 36d55a48346e..776189eeb7c3 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -211,11 +211,15 @@ import android.os.WorkSource; import android.permission.PermissionManager; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; +import android.platform.test.rule.DeniedDevices; +import android.platform.test.rule.DeviceProduct; +import android.platform.test.rule.LimitDevicesRule; import android.provider.DeviceConfig; 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; @@ -281,6 +285,7 @@ import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -313,6 +318,7 @@ import java.util.function.Consumer; @RunWith(AndroidTestingRunner.class) @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service. @RunWithLooper +@DeniedDevices(denied = {DeviceProduct.CF_AUTO}) public class NotificationManagerServiceTest extends UiServiceTestCase { private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; private static final String TEST_PACKAGE = "The.name.is.Package.Test.Package"; @@ -331,6 +337,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private final int mUid = Binder.getCallingUid(); private final @UserIdInt int mUserId = UserHandle.getUserId(mUid); + @ClassRule + public static final LimitDevicesRule sLimitDevicesRule = new LimitDevicesRule(); + @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); @@ -627,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); @@ -654,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()) { @@ -906,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); @@ -10082,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 */); @@ -13201,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/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 646ee3f6d206..1aea56cd8f9f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -80,6 +80,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.notNull; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -265,10 +266,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { .thenReturn(CUSTOM_PKG_UID); when(mPackageManager.getPackagesForUid(anyInt())).thenReturn( new String[] {pkg}); - ApplicationInfo mockAppInfo = mock(ApplicationInfo.class); - when(mockAppInfo.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL); + + ApplicationInfo appInfoSpy = spy(new ApplicationInfo()); + appInfoSpy.icon = ICON_RES_ID; + when(appInfoSpy.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL); when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt())) - .thenReturn(mockAppInfo); + .thenReturn(appInfoSpy); mZenModeHelper.mPm = mPackageManager; mZenModeEventLogger.reset(); @@ -3753,6 +3756,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { rule.zenPolicy = policy; rule.pkg = ownerPkg; rule.name = CUSTOM_APP_LABEL; + rule.iconResName = ICON_RES_NAME; + rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description, + CUSTOM_APP_LABEL); + rule.type = AutomaticZenRule.TYPE_OTHER; rule.enabled = true; return rule; } diff --git a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java index 656957c35a5d..159c760992c6 100644 --- a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java +++ b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java @@ -120,12 +120,12 @@ public class SetSandboxedTrainingDataAllowedTest { } @Test - public void setIsReceiveSandboxedTrainingDataAllowed_currentAndPreinstalledAssistant_setsOp() { + public void setShouldReceiveSandboxedTrainingData_currentAndPreinstalledAssistant_setsOp() { // Set application info so current app is the current and preinstalled assistant. mApplicationInfo.uid = Process.myUid(); mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; - mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed( + mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData( /* allowed= */ true); verify(mAppOpsManager).setUidMode(mOpIdCaptor.capture(), mUidCaptor.capture(), @@ -137,7 +137,7 @@ public class SetSandboxedTrainingDataAllowedTest { } @Test - public void setIsReceiveSandboxedTrainingDataAllowed_missingPermission_doesNotSetOp() { + public void setShouldReceiveSandboxedTrainingData_missingPermission_doesNotSetOp() { // Set application info so current app is the current and preinstalled assistant. mApplicationInfo.uid = Process.myUid(); mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; @@ -146,33 +146,33 @@ public class SetSandboxedTrainingDataAllowedTest { mPermissionEnforcer.revoke(Manifest.permission.MANAGE_HOTWORD_DETECTION); assertThrows(SecurityException.class, - () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed( + () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData( /* allowed= */ true)); verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt()); } @Test - public void setIsReceiveSandboxedTrainingDataAllowed_notPreinstalledAssistant_doesNotSetOp() { + public void setShouldReceiveSandboxedTrainingData_notPreinstalledAssistant_doesNotSetOp() { // Set application info so current app is not preinstalled assistant. mApplicationInfo.uid = Process.myUid(); mApplicationInfo.flags = ApplicationInfo.FLAG_INSTALLED; // Does not contain FLAG_SYSTEM. assertThrows(SecurityException.class, - () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed( + () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData( /* allowed= */ true)); verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt()); } @Test - public void setIsReceiveSandboxedTrainingDataAllowed_notCurrentAssistant_doesNotSetOp() { + public void setShouldReceiveSandboxedTrainingData_notCurrentAssistant_doesNotSetOp() { // Set application info so current app is not current assistant. mApplicationInfo.uid = Process.SHELL_UID; // Set current assistant uid to shell UID. mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; assertThrows(SecurityException.class, - () -> mVoiceInteractionManagerServiceStub.setIsReceiveSandboxedTrainingDataAllowed( + () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData( /* allowed= */ true)); verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt()); diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java index 8d236eda5dc5..0382ca0d9fec 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java @@ -61,6 +61,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { META_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC); META_SHORTCUTS.append(KEYCODE_S, Intent.CATEGORY_APP_MESSAGING); } + private static final int ANY_DISPLAY_ID = 123; @Before public void setUp() { @@ -96,8 +97,9 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { */ @Test public void testCtrlSpace() { - sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, 0); - mPhoneWindowManager.assertSwitchKeyboardLayout(1); + sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, /* duration= */ 0, + ANY_DISPLAY_ID); + mPhoneWindowManager.assertSwitchKeyboardLayout(/* direction= */ 1, ANY_DISPLAY_ID); } /** @@ -105,8 +107,9 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { */ @Test public void testCtrlShiftSpace() { - sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, 0); - mPhoneWindowManager.assertSwitchKeyboardLayout(-1); + sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, + /* duration= */ 0, ANY_DISPLAY_ID); + mPhoneWindowManager.assertSwitchKeyboardLayout(/* direction= */ -1, ANY_DISPLAY_ID); } /** diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java index 9cdec2588501..157d1627d993 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java @@ -114,7 +114,7 @@ class ShortcutKeyTestBase { } } - void sendKeyCombination(int[] keyCodes, long durationMillis, boolean longPress) { + void sendKeyCombination(int[] keyCodes, long durationMillis, boolean longPress, int displayId) { final long downTime = mPhoneWindowManager.getCurrentTime(); final int count = keyCodes.length; int metaState = 0; @@ -124,7 +124,7 @@ class ShortcutKeyTestBase { final KeyEvent event = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, 0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, InputDevice.SOURCE_KEYBOARD); - event.setDisplayId(DEFAULT_DISPLAY); + event.setDisplayId(displayId); interceptKey(event); // The order is important here, metaState could be updated and applied to the next key. metaState |= MODIFIER.getOrDefault(keyCode, 0); @@ -142,7 +142,7 @@ class ShortcutKeyTestBase { KeyEvent.ACTION_DOWN, keyCode, 1 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, KeyEvent.FLAG_LONG_PRESS /*flags*/, InputDevice.SOURCE_KEYBOARD); - nextDownEvent.setDisplayId(DEFAULT_DISPLAY); + nextDownEvent.setDisplayId(displayId); interceptKey(nextDownEvent); } } @@ -153,18 +153,23 @@ class ShortcutKeyTestBase { final KeyEvent upEvent = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_UP, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, InputDevice.SOURCE_KEYBOARD); - upEvent.setDisplayId(DEFAULT_DISPLAY); + upEvent.setDisplayId(displayId); interceptKey(upEvent); metaState &= ~MODIFIER.getOrDefault(keyCode, 0); } } void sendKeyCombination(int[] keyCodes, long durationMillis) { - sendKeyCombination(keyCodes, durationMillis, false /* longPress */); + sendKeyCombination(keyCodes, durationMillis, false /* longPress */, DEFAULT_DISPLAY); + } + + void sendKeyCombination(int[] keyCodes, long durationMillis, int displayId) { + sendKeyCombination(keyCodes, durationMillis, false /* longPress */, displayId); } void sendLongPressKeyCombination(int[] keyCodes) { - sendKeyCombination(keyCodes, ViewConfiguration.getLongPressTimeout(), true /* longPress */); + sendKeyCombination(keyCodes, ViewConfiguration.getLongPressTimeout(), true /* longPress */, + DEFAULT_DISPLAY); } void sendKey(int keyCode) { @@ -172,7 +177,7 @@ class ShortcutKeyTestBase { } void sendKey(int keyCode, boolean longPress) { - sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress); + sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress, DEFAULT_DISPLAY); } private void interceptKey(KeyEvent keyEvent) { diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index d057226836a3..0678210e1d2f 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -166,12 +166,34 @@ class TestPhoneWindowManager { @Mock private PhoneWindowManager.ButtonOverridePermissionChecker mButtonOverridePermissionChecker; + @Mock private IBinder mInputToken; + @Mock private IBinder mImeTargetWindowToken; + private StaticMockitoSession mMockitoSession; private OffsettableClock mClock = new OffsettableClock(); private TestLooper mTestLooper = new TestLooper(() -> mClock.now()); private HandlerThread mHandlerThread; private Handler mHandler; + private boolean mIsTalkBackEnabled; + + class TestTalkbackShortcutController extends TalkbackShortcutController { + TestTalkbackShortcutController(Context context) { + super(context); + } + + @Override + boolean toggleTalkback(int currentUserId) { + mIsTalkBackEnabled = !mIsTalkBackEnabled; + return mIsTalkBackEnabled; + } + + @Override + boolean isTalkBackShortcutGestureEnabled() { + return true; + } + } + private class TestInjector extends PhoneWindowManager.Injector { TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) { super(context, funcs, mTestLooper.getLooper()); @@ -197,6 +219,10 @@ class TestPhoneWindowManager { PhoneWindowManager.ButtonOverridePermissionChecker getButtonOverridePermissionChecker() { return mButtonOverridePermissionChecker; } + + TalkbackShortcutController getTalkbackShortcutController() { + return new TestTalkbackShortcutController(mContext); + } } TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) { @@ -304,6 +330,9 @@ class TestPhoneWindowManager { doNothing().when(mPhoneWindowManager).finishedWakingUp(anyInt(), anyInt()); doNothing().when(mPhoneWindowManager).lockNow(any()); + doReturn(mImeTargetWindowToken) + .when(mWindowManagerInternal).getTargetWindowTokenFromInputToken(mInputToken); + mPhoneWindowManager.init(new TestInjector(mContext, mWindowManagerFuncsImpl)); mPhoneWindowManager.systemReady(); mPhoneWindowManager.systemBooted(); @@ -342,12 +371,12 @@ class TestPhoneWindowManager { } long interceptKeyBeforeDispatching(KeyEvent event) { - return mPhoneWindowManager.interceptKeyBeforeDispatching(null /*focusedToken*/, - event, FLAG_INTERACTIVE); + return mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, event, + FLAG_INTERACTIVE); } void dispatchUnhandledKey(KeyEvent event) { - mPhoneWindowManager.dispatchUnhandledKey(null /*focusedToken*/, event, FLAG_INTERACTIVE); + mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE); } long getCurrentTime() { @@ -623,14 +652,16 @@ class TestPhoneWindowManager { verify(mStatusBarManagerInternal).startAssist(any()); } - void assertSwitchKeyboardLayout(int direction) { + void assertSwitchKeyboardLayout(int direction, int displayId) { mTestLooper.dispatchAll(); if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) { - verify(mInputMethodManagerInternal).switchKeyboardLayout(eq(direction)); + verify(mInputMethodManagerInternal).onSwitchKeyboardLayoutShortcut(eq(direction), + eq(displayId), eq(mImeTargetWindowToken)); verify(mWindowManagerFuncsImpl, never()).switchKeyboardLayout(anyInt(), anyInt()); } else { verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction)); - verify(mInputMethodManagerInternal, never()).switchKeyboardLayout(anyInt()); + verify(mInputMethodManagerInternal, never()) + .onSwitchKeyboardLayoutShortcut(anyInt(), anyInt(), any()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 65e77dcd4ca9..d4ba3b25178d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -124,7 +124,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { private void verifyOnActivityLaunched(ActivityRecord activity) { final ArgumentCaptor<Long> idCaptor = ArgumentCaptor.forClass(Long.class); verifyAsync(mLaunchObserver).onActivityLaunched(idCaptor.capture(), - eq(activity.mActivityComponent), anyInt()); + eq(activity.mActivityComponent), anyInt(), anyInt()); final long id = idCaptor.getValue(); setExpectedStartedId(id, activity); mLastLaunchedIds.put(activity.mActivityComponent, id); @@ -132,7 +132,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { private void verifyOnActivityLaunchFinished(ActivityRecord activity) { verifyAsync(mLaunchObserver).onActivityLaunchFinished(eq(mExpectedStartedId), - eq(activity.mActivityComponent), anyLong()); + eq(activity.mActivityComponent), anyLong(), anyInt()); } private void setExpectedStartedId(long id, Object reason) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 7aa46a62b0f1..85c6f9e9b2fe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -123,6 +123,7 @@ import android.app.ICompatCameraControlCallback; import android.app.PictureInPictureParams; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ClientTransaction; +import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.DestroyActivityItem; import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.WindowStateResizeItem; @@ -169,6 +170,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -260,8 +262,18 @@ public class ActivityRecordTests extends WindowTestsBase { final MutableBoolean pauseFound = new MutableBoolean(false); doAnswer((InvocationOnMock invocationOnMock) -> { final ClientTransaction transaction = invocationOnMock.getArgument(0); - if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) { - pauseFound.value = true; + final List<ClientTransactionItem> items = transaction.getTransactionItems(); + if (items != null) { + for (ClientTransactionItem item : items) { + if (item instanceof PauseActivityItem) { + pauseFound.value = true; + break; + } + } + } else { + if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) { + pauseFound.value = true; + } } return null; }).when(mClientLifecycleManager).scheduleTransaction(any()); @@ -279,6 +291,8 @@ public class ActivityRecordTests extends WindowTestsBase { // If the activity is not focusable, it should move to paused. activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + mClientLifecycleManager.dispatchPendingTransactions(); + assertTrue(activity.isState(PAUSING)); assertTrue(pauseFound.value); @@ -1239,7 +1253,7 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test - public void testFinishActivityIfPossible_sendResultImmediately() { + public void testFinishActivityIfPossible_sendResultImmediately() throws RemoteException { // Create activity representing the source of the activity result. final ComponentName sourceComponent = ComponentName.createRelative( DEFAULT_COMPONENT_PACKAGE_NAME, ".SourceActivity"); @@ -1270,12 +1284,8 @@ public class ActivityRecordTests extends WindowTestsBase { targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */); - try { - verify(mClientLifecycleManager, atLeastOnce()).scheduleTransaction( - any(ClientTransaction.class)); - } catch (RemoteException ignored) { - } - + verify(mClientLifecycleManager, atLeastOnce()).scheduleTransactionItem( + any(), any(ClientTransactionItem.class)); assertNull(targetActivity.results); } 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/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java index 04aa9815e698..7fdc5fc2cff6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java @@ -17,13 +17,15 @@ package com.android.server.wm; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; @@ -31,12 +33,15 @@ import android.app.IApplicationThread; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.filters.SmallTest; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -47,30 +52,47 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WmTests:ClientLifecycleManagerTests */ +// Suppress GuardedBy warning on unit tests +@SuppressWarnings("GuardedBy") @SmallTest @Presubmit public class ClientLifecycleManagerTests { + @Rule(order = 0) + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + @Rule(order = 1) + public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule(); + + @Mock + private IBinder mClientBinder; @Mock private IApplicationThread mClient; @Mock private IApplicationThread.Stub mNonBinderClient; @Mock + private ClientTransaction mTransaction; + @Mock private ClientTransactionItem mTransactionItem; @Mock private ActivityLifecycleItem mLifecycleItem; @Captor private ArgumentCaptor<ClientTransaction> mTransactionCaptor; + private WindowManagerService mWms; private ClientLifecycleManager mLifecycleManager; @Before public void setup() { MockitoAnnotations.initMocks(this); + mWms = mSystemServices.getWindowManagerService(); mLifecycleManager = spy(new ClientLifecycleManager()); + mLifecycleManager.setWindowManager(mWms); doReturn(true).when(mLifecycleItem).isActivityLifecycleItem(); + doReturn(mClientBinder).when(mClient).asBinder(); + doReturn(mNonBinderClient).when(mNonBinderClient).asBinder(); } @Test @@ -92,9 +114,11 @@ public class ClientLifecycleManagerTests { } @Test - public void testScheduleTransactionItem() throws RemoteException { - doNothing().when(mLifecycleManager).scheduleTransaction(any()); - mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem); + public void testScheduleTransactionItem_notBundle() throws RemoteException { + mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem); verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture()); ClientTransaction transaction = mTransactionCaptor.getValue(); @@ -104,7 +128,7 @@ public class ClientLifecycleManagerTests { assertNull(transaction.getTransactionItems()); clearInvocations(mLifecycleManager); - mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem); + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem); verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture()); transaction = mTransactionCaptor.getValue(); @@ -113,9 +137,54 @@ public class ClientLifecycleManagerTests { } @Test - public void testScheduleTransactionAndLifecycleItems() throws RemoteException { - doNothing().when(mLifecycleManager).scheduleTransaction(any()); - mLifecycleManager.scheduleTransactionAndLifecycleItems(mClient, mTransactionItem, + public void testScheduleTransactionItem() throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + spyOn(mWms.mWindowPlacerLocked); + doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled(); + + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem); + + // When there is traversal scheduled, add transaction items to pending. + assertEquals(1, mLifecycleManager.mPendingTransactions.size()); + ClientTransaction transaction = + mLifecycleManager.mPendingTransactions.get(mNonBinderClient); + assertEquals(1, transaction.getTransactionItems().size()); + assertEquals(mTransactionItem, transaction.getTransactionItems().get(0)); + assertNull(transaction.getCallbacks()); + assertNull(transaction.getLifecycleStateRequest()); + verify(mLifecycleManager, never()).scheduleTransaction(any()); + + // Add new transaction item to the existing pending. + clearInvocations(mLifecycleManager); + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem); + + assertEquals(1, mLifecycleManager.mPendingTransactions.size()); + transaction = mLifecycleManager.mPendingTransactions.get(mNonBinderClient); + assertEquals(2, transaction.getTransactionItems().size()); + assertEquals(mTransactionItem, transaction.getTransactionItems().get(0)); + assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1)); + assertNull(transaction.getCallbacks()); + assertNull(transaction.getLifecycleStateRequest()); + verify(mLifecycleManager, never()).scheduleTransaction(any()); + } + + @Test + public void testScheduleTransactionItemUnlocked() throws RemoteException { + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionItemUnlocked(mNonBinderClient, mTransactionItem); + + // Dispatch immediately. + assertTrue(mLifecycleManager.mPendingTransactions.isEmpty()); + verify(mLifecycleManager).scheduleTransaction(any()); + } + + @Test + public void testScheduleTransactionAndLifecycleItems_notBundle() throws RemoteException { + mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionAndLifecycleItems(mNonBinderClient, mTransactionItem, mLifecycleItem); verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture()); @@ -124,4 +193,36 @@ public class ClientLifecycleManagerTests { assertEquals(mTransactionItem, transaction.getCallbacks().get(0)); assertEquals(mLifecycleItem, transaction.getLifecycleStateRequest()); } + + @Test + public void testScheduleTransactionAndLifecycleItems() throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + spyOn(mWms.mWindowPlacerLocked); + doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled(); + + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionAndLifecycleItems(mNonBinderClient, mTransactionItem, + mLifecycleItem); + + assertEquals(1, mLifecycleManager.mPendingTransactions.size()); + final ClientTransaction transaction = + mLifecycleManager.mPendingTransactions.get(mNonBinderClient); + assertEquals(2, transaction.getTransactionItems().size()); + assertEquals(mTransactionItem, transaction.getTransactionItems().get(0)); + assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1)); + assertNull(transaction.getCallbacks()); + assertNull(transaction.getLifecycleStateRequest()); + verify(mLifecycleManager, never()).scheduleTransaction(any()); + } + + @Test + public void testDispatchPendingTransactions() throws RemoteException { + mLifecycleManager.mPendingTransactions.put(mClientBinder, mTransaction); + + mLifecycleManager.dispatchPendingTransactions(); + + assertTrue(mLifecycleManager.mPendingTransactions.isEmpty()); + verify(mTransaction).schedule(); + verify(mTransaction).recycle(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 50fe0425fe9c..1fb7cd8e6e1c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -553,7 +553,7 @@ public class DragDropControllerTests extends WindowTestsBase { assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(), new InputChannel(), true /* isDragDrop */)); mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient, - flag, surface, 0, 0, 0, 0, 0, data); + flag, surface, 0, 0, 0, 0, 0, 0, 0, data); assertNotNull(mToken); r.run(); @@ -575,7 +575,7 @@ public class DragDropControllerTests extends WindowTestsBase { private void startA11yDrag(int flags, ClipData data, Runnable r) { mToken = mTarget.performDrag(0, 0, mWindow.mClient, - flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, data); + flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, 0, 0, data); assertNotNull(mToken); r.run(); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 6e4f13a75625..c902d4597571 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1569,13 +1569,13 @@ public class VoiceInteractionManagerService extends SystemService { @Override @EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) - public void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed) { - super.setIsReceiveSandboxedTrainingDataAllowed_enforcePermission(); + public void setShouldReceiveSandboxedTrainingData(boolean allowed) { + super.setShouldReceiveSandboxedTrainingData_enforcePermission(); synchronized (this) { if (mImpl == null) { throw new IllegalStateException( - "setIsReceiveSandboxedTrainingDataAllowed without running voice " + "setShouldReceiveSandboxedTrainingData without running voice " + "interaction service"); } 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/android/telephony/satellite/NtnSignalStrength.java b/telephony/java/android/telephony/satellite/NtnSignalStrength.java index 16d765455d21..2fec423d1d65 100644 --- a/telephony/java/android/telephony/satellite/NtnSignalStrength.java +++ b/telephony/java/android/telephony/satellite/NtnSignalStrength.java @@ -86,6 +86,9 @@ public final class NtnSignalStrength implements Parcelable { readFromParcel(in); } + /** + * Returns notified non-terrestrial network signal strength level. + */ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @NtnSignalStrengthLevel public int getLevel() { return mLevel; diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 71786b308937..e09bd201f93e 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -34,6 +34,7 @@ import android.os.ICancellationSignal; import android.os.OutcomeReceiver; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceSpecificException; import android.telephony.SubscriptionManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyFrameworkInitializer; @@ -1919,7 +1920,6 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - @NonNull public void requestNtnSignalStrength(@NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<NtnSignalStrength, SatelliteException> callback) { Objects.requireNonNull(executor); @@ -1962,6 +1962,8 @@ public final class SatelliteManager { /** * Registers for NTN signal strength changed from satellite modem. + * If the registration operation is not successful, a {@link SatelliteException} that contains + * {@link SatelliteResult} will be thrown. * * <p> * Note: This API is specifically designed for OEM enabled satellite connectivity only. @@ -1973,16 +1975,14 @@ public final class SatelliteManager { * @param executor The executor on which the callback will be called. * @param callback The callback to handle the NTN signal strength changed event. * - * @return The {@link SatelliteResult} result of the operation. - * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * @throws SatelliteException if the callback registration operation fails. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - @SatelliteResult public int registerForNtnSignalStrengthChanged( - @NonNull @CallbackExecutor Executor executor, - @NonNull NtnSignalStrengthCallback callback) { + public void registerForNtnSignalStrengthChanged(@NonNull @CallbackExecutor Executor executor, + @NonNull NtnSignalStrengthCallback callback) throws SatelliteException { Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -1999,16 +1999,18 @@ public final class SatelliteManager { ntnSignalStrength))); } }; + telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback); sNtnSignalStrengthCallbackMap.put(callback, internalCallback); - return telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback); } else { throw new IllegalStateException("Telephony service is null."); } + } catch (ServiceSpecificException ex) { + logd("registerForNtnSignalStrengthChanged() registration fails: " + ex.errorCode); + throw new SatelliteException(ex.errorCode); } catch (RemoteException ex) { loge("registerForNtnSignalStrengthChanged() RemoteException: " + ex); ex.rethrowFromSystemServer(); } - return SATELLITE_RESULT_REQUEST_FAILED; } /** @@ -2025,6 +2027,8 @@ public final class SatelliteManager { * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}. * * @throws SecurityException if the caller doesn't have required permission. + * @throws IllegalArgumentException if the callback is not valid or has already been + * unregistered. * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @@ -2041,6 +2045,7 @@ public final class SatelliteManager { telephony.unregisterForNtnSignalStrengthChanged(mSubId, internalCallback); } else { loge("unregisterForNtnSignalStrengthChanged: No internal callback."); + throw new IllegalArgumentException("callback is not valid"); } } else { throw new IllegalStateException("Telephony service is null."); diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index d7d28a17e7f2..37752d0cecd8 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -3101,16 +3101,21 @@ interface ITelephony { void requestNtnSignalStrength(int subId, in ResultReceiver receiver); /** - * Registers for NTN signal strength changed from satellite modem. + * Registers for NTN signal strength changed from satellite modem. If the registration operation + * is not successful, a {@link SatelliteException} that contains {@link SatelliteResult} will be + * thrown. * * @param subId The subId of the subscription to request for. - * @param callback The callback to handle the NTN signal strength changed event. - * - * @return The {@link SatelliteResult} result of the operation. + * @param callback The callback to handle the NTN signal strength changed event. If the + * operation is successful, {@link NtnSignalStrengthCallback#onNtnSignalStrengthChanged( + * NtnSignalStrength)} will return an instance of {@link NtnSignalStrength} with a value of + * {@link NtnSignalStrength.NtnSignalStrengthLevel} when the signal strength of non-terrestrial + * network has changed. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForNtnSignalStrengthChanged(int subId, in INtnSignalStrengthCallback callback); + void registerForNtnSignalStrengthChanged(int subId, + in INtnSignalStrengthCallback callback); /** * Unregisters for NTN signal strength changed from satellite modem. @@ -3177,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. @@ -3191,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 { diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp index 15a6a207d6d1..6fcdf1c77704 100644 --- a/tools/aapt2/integration-tests/SymlinkTest/Android.bp +++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp @@ -27,4 +27,7 @@ android_test { name: "AaptSymlinkTest", sdk_version: "current", use_resource_processor: false, + compile_data: [ + "targets/*", + ], } diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java index 3bcabcb01c5e..d63bff6f4da3 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java @@ -343,6 +343,28 @@ public class Parcel_host { p.mPos += length; p.updateSize(); } + public static int nativeCompareData(long thisNativePtr, long otherNativePtr) { + var a = getInstance(thisNativePtr); + var b = getInstance(otherNativePtr); + if ((a.mSize == b.mSize) && Arrays.equals(a.mBuffer, b.mBuffer)) { + return 0; + } else { + return -1; + } + } + public static boolean nativeCompareDataInRange( + long ptrA, int offsetA, long ptrB, int offsetB, int length) { + var a = getInstance(ptrA); + var b = getInstance(ptrB); + if (offsetA < 0 || offsetA + length > a.mSize) { + throw new IllegalArgumentException(); + } + if (offsetB < 0 || offsetB + length > b.mSize) { + throw new IllegalArgumentException(); + } + return Arrays.equals(Arrays.copyOfRange(a.mBuffer, offsetA, offsetA + length), + Arrays.copyOfRange(b.mBuffer, offsetB, offsetB + length)); + } public static void nativeAppendFrom( long thisNativePtr, long otherNativePtr, int srcOffset, int length) { var dst = getInstance(thisNativePtr); |